ARC 105

A - Fourtune Cookies

题目大意:

  给你\(A,B,C,D\)四个数字,问你是否存在几个数相加等于另外几个数.c++

思路:

  过于简单.数组

B - MAX-=min

题目大意:

  给你\(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;

C - Camels and Bridge

题目大意:

  有\(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;
}

D - Let's Play Nim

题目大意:

  你如今有\(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;
}

E - Keep Graph Disconnected

题目大意:

  给你一个\(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;
}

F - Lights Out on Connected Graph

题目大意:

  给你一个\(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;
}
相关文章
相关标签/搜索