哭了啊,又被同机房那几个奆佬摁在地上摩擦,平均比每人少做一道题目。
比赛链接:https://codeforc.es/contest/1445
题意:给你两个数组\(a,b\),让你判断能不能通过对\(b\)重新的排序,让其满足:\(a_{i}+b_{i}≤k(1≤i≤n)\),其中\(k\)是给定的常数。
做法:不难发现,\(a\)升序,\(b\)降序,然后暴力做即可。
时间复杂度:\(O(nlogn)\)
#include<cstdio>
#include<cstring>
#define N 110
using namespace std;
int a[N],b[N],n,k;
int main()
{
int T;scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++)scanf("%d",&b[i]);
bool bk=0;
for(int i=1;i<=n;i++)
{
if(a[i]+b[n-i+1]>k)
{
bk=1;
break;
}
}
if(!bk)printf("Yes\n");
else printf("No\n");
}
return 0;
}
题意: 一场比赛有不知道多少人(>100)个人参与,在淘汰赛有两场比赛,两场比赛的分数和总分数都按按降序排序,如果有同分按字典序排序,然后现在两场的分数排名给出来,但是不知道具体的分数,问你最后总排名第\(100\)名的分数最少能是多少。
当然,两场比赛的分数还是知道一点信息的,第一场比赛第\(100\)名是\(a\)分,然后前\(100\)名在第二场比赛至少得了\(b(b≤c)\)分,第二场比赛第\(100\)名是\(c\)分,前\(100\)名在第一场比赛至少得了\(d(d≤a)\)分。
做法:艹,题意就看了半天,贪心:首先为了第\(100\)名分数最小,肯定第一场比赛前\(100\)名都拿\(a\)分,然后这些人在第二场比赛都拿了\(b\)分,第二场比赛前\(100\)名都拿\(c\)分,在第一场比赛都拿\(d\)分能保证第\(100\)名分数最小。
时间复杂度:\(O(1)\)
题意:给你\(q,p\),求最大的整数\(x\),满足\(x\)整数\(1\)但没有被\(p\)整除。
做法:把\(p\)质因数分解为:\(a_{1}^{b_{1}}a_2^{b_2}...a_{k}^{b_{k}}\),然后设\(x=q\),只要\(x\)中含任意一个\(a_{i}\)因子个数的数量小于\(b_{i}\)即可,用一个变量记录让哪个因子小于\(b_{i}\)的代价最小,最后除一下就行了。
时间复杂度:\(O(很小)\)
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long LL;
inline LL ksm(LL x,LL y)
{
LL ans=1;
for(LL i=1;i<=y;i++)ans*=x;
return ans;
}
inline LL mymin(LL x,LL y){return x<y?x:y;}
int main()
{
// freopen("std.in","r",stdin);
// freopen("vio.out","w",stdout);
int T;
scanf("%d",&T);
while(T--)
{
LL x,y;
scanf("%lld%lld",&x,&y);
LL ed=sqrt(y)+1;
LL ans=x;
LL shit=(LL)999999999999999999;//赛后才发现这个地方少打了几个9
for(LL i=2;i<=ed;i++)
{
if(y%i==0)
{
LL cnt=0;
while(y%i==0)y/=i,cnt++;
LL pre=0;
while(x%i==0)x/=i,pre++;
if(pre<cnt)
{
shit=1;
break;
}
else shit=mymin(ksm(i,pre-cnt+1),shit);
}
}
if(y>1 && shit>1)
{
LL pre=0;
while(x%y==0)x/=y,pre++;
if(pre<1)shit=1;
else shit=mymin(ksm(y,pre),shit);
}
printf("%lld\n",ans/shit);
}
return 0;
}
题意: 给你长度为\(2n\)的数组,将其分成两个数组\(q,p\),然后\(q\)从大到小排序,\(p\)从小到大排序,这次拆分的价值为:\(\sum\limits_{i=1}^{n}|q_{i}-p_{i}|\)。
然后问你所有拆分的价值,模\(998244353\)。
做法:额,首先,如果所有数字不同,不难发现,不管你怎么分,最大的那\(n\)个数字刚好分在\(q,p\)大的那一端,无法互相减去,所以不管你怎么分,价值都等于最大的\(n\)个数字减最小的\(n\)个数字,那万一数字相同呢?我们认为同样的数字,在原数组中所处下标越小,其就越小,且在排序的时候比较大小也这么认为,那么一样可以得到同样的结论,然后只需要乘上拆分的个数即可。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 310000
using namespace std;
typedef long long LL;
LL mod=998244353;
inline LL ksm(LL x,LL y)
{
LL ans=1;
while(y)
{
if(y&1)ans=(ans*x)%mod;
x=(x*x)%mod;y>>=1;
}
return ans;
}
LL a[N],fc[N],sum;
int n;
int main()
{
scanf("%d",&n);
int ed=n*2;
for(int i=1;i<=ed;i++)scanf("%d",&a[i]);
fc[0]=1;for(int i=1;i<=ed;i++)fc[i]=(LL)(fc[i-1]*i)%mod;
sort(a+1,a+ed+1);
for(int i=1;i<=n;i++)sum+=a[n+i]-a[i];
sum%=mod;
LL shit=ksm(fc[n],mod-2);
printf("%lld\n",sum*shit%mod*shit%mod*fc[ed]%mod);
return 0;
}
题意: 给你\(n\)个点,\(m\)条边,然后每个点都属于一个学术团队,有\(k\)个学术团队,求满足要求的二元组\((i,j)\),要求为:\(i<j\),且第\(i\)个团队和第\(j\)个团队形成的诱导子图为二分图。
做法:
做法1: 先默认所有二元组都可以,找不可以的,只要用并查集先处理出每个团队自己的,然后再针对每个团队和其他团队的即可。
然后再两个团队判断完之后记得还原并查集,当然,需要注意的时,并查集不能路径压缩,不然还原并查集的时间复杂度就不是\(O(m)\)的了,只能按秩合并。
时间复杂度:\(O(mlogn)\)
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define N 510000
using namespace std;
typedef long long LL;
int fa[N],val[N],siz[N],zanval;
int findfa(int x)
{
zanval=0;
int y=x;
while(fa[y]!=y)zanval^=val[y],y=fa[y];
return y;
}
inline bool mer(int x,int y,int type)//合并
{
int tx=findfa(x);type^=zanval;
int ty=findfa(y);type^=zanval;
if(tx==ty)
{
if(type==1)return 0;
}
else
{
if(siz[tx]>siz[ty])fa[ty]=tx,siz[tx]+=siz[ty],val[ty]=type;
else fa[tx]=ty,siz[ty]+=siz[tx],val[tx]=type;
}
return 1;
}
struct node
{
int id;
int x,y;
node(int idx=0,int xx=0,int yx=0){id=idx;x=xx;y=yx;}
};
vector<node> fuck[N];//每个团队向其余团队的边
node sta[N];int top;
int n,m,k,be[N];
bool shit[N]/*判断一个联通块本身自己是不是二分图*/;int shitcnt;//合法的团队
bool tis[N];
int pre[N],pre_top;
inline void check(int x)//判断这个点的祖先完事之后是否需要还原
{
x=findfa(x);
if(!tis[x])
{
tis[x]=1;
pre[++pre_top]=x;
}
}
inline bool solve(int l,int r)//判断两个团队是否是二分图
{
pre_top=0;
for(int i=l;i<=r;i++)
{
int x=sta[i].x,y=sta[i].y;
check(x);check(y);
if(!mer(x,y,1))return 1;
}
return 0;
}
inline void put_hui()//恢复并查集
{
for(int i=1;i<=pre_top;i++)fa[pre[i]]=pre[i],val[pre[i]]=0,tis[pre[i]]=0;
}
inline bool cmp(node x,node y){return x.id<y.id;}
int main()
{
scanf("%d%d%d",&n,&m,&k);
shitcnt=k;
for(int i=1;i<=n;i++){scanf("%d",&be[i]);fa[i]=i;siz[i]=1;}
for(int i=1;i<=m;i++)
{
int x,y;scanf("%d%d",&x,&y);
if(be[x]==be[y])
{
if(!shit[be[x]])
{
if(!mer(x,y,1))
{
shit[be[x]]=1;
shitcnt--;
}
}
}
else
{
fuck[be[x]].push_back(node(be[y],x,y));
fuck[be[y]].push_back(node(be[x],y,x));
}
}
LL ans=(LL)shitcnt*(shitcnt-1)/2;
LL fei=0;
for(int i=1;i<=k;i++)
{
if(shit[i])continue;
top=fuck[i].size();
for(int j=0;j<top;j++)sta[j+1]=fuck[i][j];
sort(sta+1,sta+top+1,cmp);//使得id非严格单调递增
int tmp=sta[1].id,tmppre=1;
for(int j=1;j<=top;j++)
{
if(sta[j].id!=tmp)
{
if(!shit[tmp])
{
fei+=solve(tmppre,j-1);
put_hui();
}
tmppre=j;
tmp=sta[j].id;
}
}
if(top && !shit[sta[top].id])
{
fei+=solve(tmppre,top);
put_hui();
}
}
printf("%lld\n",ans-fei/2/*别忘了除2*/);
return 0;
}
做法2:先Orz 一波ZWQ,其会\(O(n+m)\)的做法,首先,对每个团队跑一遍二分图染色,处理出这个团队必须对立的两个部分,例如\(1,3\)和\(2,4\)必须对立,那么\(1,3\)缩成一个点\(a\),\(2,4\)缩成一个点\(b\),\(a,b\)连边(当然,为了保证时间复杂度是正确的,建议用\(match\)数组记录这条边,防止用边目录),需要注意的是,团队里可能不止一个必须对立的两个部分。然后如果\(1,3\)缩成了\(a\)点,那么其连边都变成\(a\)点连的边,最后只需要判断两个集合之间是否存在奇数环即可(二分图染色),当然,稍微注意一些细节:两个团队之间的边用\(vector\)存,只对两个团队之间边的端点跑二分图等等。
这样,就能保证时间复杂度:\(O(n+m)\)。
当然,我是听\(ZWQ\)讲完口胡的。。。
原文:https://www.cnblogs.com/zhangjianjunab/p/13915329.html