给你\(A,B,C,D\)四个数字,问你是否存在几个数相加等于另外几个数.c++
过于简单.数组
给你\(n\)个数,每次操做选择最大的数\(X\),和最小的数\(x\),让全部最大的数减去一个\(x\).问最后当全部数都相等时的数字为多少.cookie
能够发现这个操做至关于求\(\gcd\)的展转相减法,因而咱们只用求全部数的\(\gcd\)便可.spa
n=rd();int ans=0; for(int i=1; i<=n; ++i) { A[i]=rd(); if(i^1)ans=gcd(A[i],ans); else ans=A[1]; }cout<<ans;
有\(n\)只骆驼,每只骆驼都有一个重量\(w_i\),它们要通过一座桥.桥被分红\(m\)段,每段有一个长度\(l_i\)和最大限重\(v_i\),咱们能够在过桥前安排骆驼的顺序,求最远两个骆驼相距的最小距离.code
首先暴力枚举一遍骆驼的先后顺序,这时咱们能够求出任意两只骆驼的间的最小距离(预处理一个前缀最大值),而后作一个小\(dp\)就行了.复杂度\(O(n!n^2\log m)\)游戏
#include<bits/stdc++.h> using namespace std; const int M=1e5+5; int n,m; int w[10],p[10]; int A[10]; struct info { int x,y; bool operator <(const info &_)const { if(y^_.y)return y<_.y; return x<_.x; } }F[M]; int Mx[M],dp[10]; int main(){ n=rd(),m=rd(); for(int i=1; i<=n; ++i)w[i]=rd(),p[i]=i; for(int i=1; i<=m; ++i)F[i].x=rd(),F[i].y=rd(); sort(F+1,F+m+1); for(int i=1; i<=m; ++i) { Mx[i]=F[i].x; if(i>1)Max(Mx[i],Mx[i-1]); } int Ans=1e9; do { for(int i=1; i<=n; ++i)A[p[i]]=w[i],dp[i]=0; for(int i=1; i<=n; ++i) { if(A[i]>F[1].y)return puts("-1"),0; int sum=A[i];dp[i]=dp[i-1]; for(int j=i-1; j; --j) { sum+=A[j]; int p=lower_bound(F+1,F+m+1,(info)<%-1,sum%>)-F-1; if(p)Max(dp[i],dp[j]+Mx[p]); } }Min(Ans,dp[n]); }while(next_permutation(p+1,p+n+1)); printf("%d",Ans); return 0; }
你如今有\(n\)个背包,\(n\)个盘子,如今背包\(i\)有\(A[i]\)枚硬币.如今有\(X\),\(Y\)两我的在玩游戏,若是任何一个背包里还存在硬币,那么当前这我的必须选择一个背包,将里面的全部硬币倒入任意一个盘子.若是没有一个背包还存在硬币,那么这我的至少选择一枚硬币从一个盘子移出.若是一我的没法操做,那么他就输了.get
能够看出若是背包里的硬币全转移到了盘子上,那么就变成了\(nim\),若异或和为\(0\),则先手输,由背包个数奇偶能够知道谁会是先手.
若背包个数为奇,则第二我的会是先手,能够发现不管第一我的怎么操做,总能会使一堆硬币个数超过\(sum/2\),这样最后异或和一定不为\(0\),即第二人必胜.
若背包个数为偶(其实跟上面无太大区别),会多一种状况,就是出现异或和为\(0\),也就是说咱们须要判断每种硬币个数的背包数为偶.it
#include<bits/stdc++.h> using namespace std; bool f1; #define LL long long inline LL rd() { LL res=0;bool f=0; char ch; while(ch=getchar(),ch<48||ch>57)if(ch=='-')f=true; do res=(res<<1)+(res<<3)+(ch^48); while(ch=getchar(),ch>47&&ch<58); return f?-res:res; } const int M=1e5+5; int A[M]; bool f2; int main(){ int T=rd(); while(T--) { int n=rd(); for(int i=1; i<=n; ++i)A[i]=rd(); if(n&1){puts("Second");continue;} sort(A+1,A+n+1); bool flag=true; for(int i=1; i<=n; i+=2)if(A[i]!=A[i+1]){puts("First");flag=false;break;} if(flag)puts("Second"); } return 0; }
给你一个\(n\)个节点,\(m\)条边的无向图\(G\),若是一个图为好图,那么它须要知足两个条件:
1. 节点\(1\)和\(n\)不连通.
2. 没有重边和自环.
如今有两我的\(Taro\),\(Jiro\)玩游戏,每一个人每一回合能够在图\(G\)上连条边,当一我的的连边使得图\(G\)不为好图,那么这我的就输了.io
归纳一下博弈论:若是我能赢就是敌不动我不动,不然寻找转折点.
回到此题,首先将原图缩成若干个联通块,对于联通块内的点咱们能够将其连成一个彻底图,这根本不会影响胜负.
假设咱们只连联通块内的边,若边数为奇,则先手赢.
考虑链接联通块间的边,思考何时会改变战局.发现只有将两个奇数点的联通块相连才会新增偶数条边,这就是一个转折点,而新生成的联通块点数变成偶数.那么显然咱们须要统计奇数点的联通块个数\(cnt\).
若\(cnt\)为奇数,那么会转折\(cnt/2\)次.
若\(cnt\)为偶数,存在一种特殊状况,可能最后\(1\)和\(n\)所在的联通块点数为奇.咱们须要判断怎样才会产生这种状况,咱们能够从奇偶考虑.
当初始时\(1\),\(n\)所在联通块点数都为奇数时,能够发现前后手均可以扭转战局.
当初始时\(1\),\(n\)所在联通块点数都为偶数时,能够发现前后手都不能够扭转战局.
当初始时\(1\),\(n\)所在联通块点数一奇一偶时,只有先手能够扭转战局,也就是说先手必赢.cookies
#include<bits/stdc++.h> using namespace std; const int M=1e5+5; int T; int n,m; int fa[M],Sz[M],A[M]; int find(int g) {return fa[g]==g?g:fa[g]=find(fa[g]);} void Solve(int cur) { if(cur&1)puts("First"); else puts("Second"); } int main(){ T=rd(); while(T--) { n=rd(),m=rd(); for(int i=1; i<=n; ++i)fa[i]=i,Sz[i]=1,A[i]=0; for(int i=1; i<=m; ++i) { int u=rd(),v=rd(); int sx=find(u),sy=find(v); if(sx^sy)A[sy]+=A[sx],fa[sx]=sy,Sz[sy]+=Sz[sx]; A[sy]++; } LL res=0,cnt=0; bool f1=false,f2=false; for(int i=1; i<=n; ++i) if(fa[i]==i) { res+=1LL*(Sz[i]-1)*Sz[i]/2-A[i]; if(Sz[i]&1) { cnt++; if(find(1)==i)f1=true; if(find(n)==i)f2=true; } } if(find(1)==find(n)){puts("Second");continue;} if(res&1) { if(cnt&1)Solve(cnt/2+1); else { if(f1&&f2)Solve(cnt/2); else if(!f1&&!f2)Solve(cnt/2+1); else puts("First"); } }else { if(cnt&1)Solve(cnt/2); else { if(!f1&&!f2)Solve(cnt/2); else if(f1&&f2)Solve(cnt/2+1); else puts("First"); } } } return 0; }
给你一个\(n\)个点,\(m\)条边的无向连通简单图\(G\).你能够从中选择边构造一个新图\(G_2\),使得这个新图的点是连通的,而且是个二分图.询问选边的方案数.
彻底不知道该怎么构造一个二分图.其实咱们能够根据它的定义来构造,咱们能够分开两组点,使得一组点向另外一组点连边.若是咱们随意连边,咱们没法保证这张图是联通的.
想到先定义一个辅助数组\(f[i]\)表示\(i\)点集组成二分图但不保证联通的方案数,显然求法就是枚举一组点,将其往另外一组点内随意连边,组内不能连边便可.\(f[i]=\sum_{k\subset i}2^{edge[i]-edge[k]-edge[i\bigoplus k]}\)
对于最终答案咱们也来一个数组\(dp[i]\),容易想到咱们能够枚举子集,将不合法的答案算出来最后减去.为了防止计算重复,咱们能够先选择一个点,枚举的联通子集都必须包含这个点便可.\(dp[i]=f[i]-\sum_{k\subset i}dp[k]*f[i\bigoplus k]\).
最后答案应该除以\(2\),由于咱们构造二分图的时候,同一个图由于染上了不一样颜色,会计算两次.
#include<bits/stdc++.h> using namespace std; const int M=1<<17; const int P=998244353; int n,m; int dp[M],f[M]; int edge[M]; int A[M],B[M]; int Pow[M]; bool vis[M]; int main(){ n=rd(),m=rd();Pow[0]=1; for(int i=1; i<=m; ++i)A[i]=rd(),B[i]=rd(); for(int i=1; i<=m; ++i)Pow[i]=Pow[i-1]*2%P; for(int i=0; i<1<<n; ++i) { for(int j=1; j<=m; ++j) if((i&(1<<A[j]-1))&&(i&(1<<B[j]-1)))edge[i]++; f[i]=1; for(int j=i; j; j=(j-1)&i) f[i]=(f[i]+Pow[edge[i]-edge[j]-edge[i^j]])%P; dp[i]=f[i]; int k=i&-i; for(int j=(i-1)&i; j; j=(j-1)&i) if(j&k)dp[i]=(dp[i]-1LL*f[i^j]*dp[j]%P+P)%P; }printf("%d\n",1LL*dp[(1<<n)-1]*(P+1)/2%P); return 0; }