AtCoder Grand Contest 002

正题

A - Range Product

考虑一下正负就可以了。

#include<bits/stdc++.h>
using namespace std;

int a,b;

int main(){
	scanf("%d %d",&a,&b);
	if(a<=0 && b>=0) printf("Zero");
	else if(a>0) printf("Positive");
	else printf(((b-a+1)&1)?"Negative":"Positive");
}

B - Box and Ball

维护一下当前每一个地方是否可能有红球,以及有多少个球,然后转移的时候,球的个数可以直接转移,是否有红球就或一下,如果拿走之后不剩球了,那么这个地方也改成不可能有红球。

#include<bits/stdc++.h>
using namespace std;

const int N=100010;
bool tf[N];
int a[N],n,m;

int main(){
	scanf("%d %d",&n,&m);tf[1]=true;
	for(int i=1;i<=n;i++) a[i]=1;
	int x,y;
	for(int i=1;i<=m;i++){
		scanf("%d %d",&x,&y);
		a[x]--;a[y]++;
		if(tf[x]) tf[y]=true;
		if(!a[x]) tf[x]=false;
	}
	int ans=0;
	for(int i=1;i<=n;i++) ans+=tf[i];
	printf("%d\n",ans);
}

C - Knot Puzzle

考虑有解的充要条件是存在相邻两个相加 ≥ k \geq k k,考虑反证法即可证明,构造方案也十分简单,从两边减就可以了。

#include<bits/stdc++.h>
using namespace std;

const int N=100010;
int a[N],n,m;

int main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	int pos=0;
	for(int i=1;i<n;i++) if(a[i]+a[i+1]>=m) {pos=i;break;}
	if(!pos) printf("Impossible");
	else{
		printf("Possible\n");
		for(int i=1;i<pos;i++) printf("%d\n",i);
		for(int i=n-1;i>pos;i--) printf("%d\n",i);
		printf("%d\n",pos);
	}
}

D - Stamp Rally

#include<bits/stdc++.h>
using namespace std;

const int N=100010;
struct node{
	int x,y,z,ans;
}w[N];
pair<int,int> p[N];
int n,m,q,fa[N],sz[N];

int findpa(int x){return fa[x]!=x?findpa(fa[x]):x;}

void solve(vector<int>&A,int l,int r){
	if(A.size()==0) return ;
	if(l==r){
		for(int i=0;i<A.size();i++) w[A[i]].ans=l;
		return ;
	}
	vector<int> L,R;
	vector<pair<int,int> > op;
	int mid=(l+r)/2;
	for(int i=l;i<=mid;i++) {
		int fx=findpa(p[i].first),fy=findpa(p[i].second);
		if(fx!=fy){
			if(sz[fx]>sz[fy]) swap(fx,fy);
			op.push_back(make_pair(fx,fy));
			fa[fx]=fy;sz[fy]+=sz[fx];
		}
	}
	for(int i=0;i<A.size();i++){
		int fx=findpa(w[A[i]].x),fy=findpa(w[A[i]].y);
		bool we;
		if(fx==fy) we=(sz[fx]>=w[A[i]].z);
		else we=(sz[fx]+sz[fy]>=w[A[i]].z);
		if(we) L.push_back(A[i]);
		else R.push_back(A[i]);
	}
	solve(R,mid+1,r);
	for(int i=op.size()-1;i>=0;i--) fa[op[i].first]=op[i].first,sz[op[i].second]-=sz[op[i].first];
	solve(L,l,mid);
}

int main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++) sz[i]=1,fa[i]=i;
	for(int i=1;i<=m;i++) scanf("%d %d",&p[i].first,&p[i].second);
	scanf("%d",&q);
	vector<int> A;
	for(int i=1;i<=q;i++) scanf("%d %d %d",&w[i].x,&w[i].y,&w[i].z),A.push_back(i);
	solve(A,1,m);
	for(int i=1;i<=q;i++) printf("%d\n",w[i].ans);
}

D - Stamp Rally

发现答案是可二分的,考虑整体二分,只需要使用带撤销的并查集来维护就好了。

#include<bits/stdc++.h>
using namespace std;

const int N=100010;
struct node{
	int x,y,z,ans;
}w[N];
pair<int,int> p[N];
int n,m,q,fa[N],sz[N];

int findpa(int x){return fa[x]!=x?findpa(fa[x]):x;}

void solve(vector<int>&A,int l,int r){
	if(A.size()==0) return ;
	if(l==r){
		for(int i=0;i<A.size();i++) w[A[i]].ans=l;
		return ;
	}
	vector<int> L,R;
	vector<pair<int,int> > op;
	int mid=(l+r)/2;
	for(int i=l;i<=mid;i++) {
		int fx=findpa(p[i].first),fy=findpa(p[i].second);
		if(fx!=fy){
			if(sz[fx]>sz[fy]) swap(fx,fy);
			op.push_back(make_pair(fx,fy));
			fa[fx]=fy;sz[fy]+=sz[fx];
		}
	}
	for(int i=0;i<A.size();i++){
		int fx=findpa(w[A[i]].x),fy=findpa(w[A[i]].y);
		bool we;
		if(fx==fy) we=(sz[fx]>=w[A[i]].z);
		else we=(sz[fx]+sz[fy]>=w[A[i]].z);
		if(we) L.push_back(A[i]);
		else R.push_back(A[i]);
	}
	solve(R,mid+1,r);
	for(int i=op.size()-1;i>=0;i--) fa[op[i].first]=op[i].first,sz[op[i].second]-=sz[op[i].first];
	solve(L,l,mid);
}

int main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++) sz[i]=1,fa[i]=i;
	for(int i=1;i<=m;i++) scanf("%d %d",&p[i].first,&p[i].second);
	scanf("%d",&q);
	vector<int> A;
	for(int i=1;i<=q;i++) scanf("%d %d %d",&w[i].x,&w[i].y,&w[i].z),A.push_back(i);
	solve(A,1,m);
	for(int i=1;i<=q;i++) printf("%d\n",w[i].ans);
}

E - Candy Piles

考虑排序后把它看成一堆方块叠起来,第 i i i列有 a i a_i ai个方块。
两种操作可以看做每次看做删掉最后一行或者最后一列,谁操作最后一步谁就输了,转化问题为当前有个棋子在右下角,每次可以向左或者向上移一步,谁移动了最后一步谁就获胜。
一个点是必胜点当且仅当走一步可以走到至少一个必败点,一个点是必败点当且仅当走一步走到的都是必胜点。
( i , j ) (i,j) (i,j)为必败点,那么 ( i − 1 , j + 1 ) (i-1,j+1) (i1,j+1)为必败点,这个显然,因为 ( i − 1 , j ) , ( i , j + 1 ) (i-1,j),(i,j+1) (i1,j),(i,j+1)都是必胜点。
( i , j ) (i,j) (i,j)为必胜点,那么 ( i − 1 , j + 1 ) (i-1,j+1) (i1,j+1)为必胜点,考虑从左到右,从上到下归纳就可以了, ( i , j ) (i,j) (i,j)是必胜点,说明 ( i + 1 , j ) (i+1,j) (i+1,j) ( i , j − 1 ) (i,j-1) (i,j1)至少有一个必败点,所以 ( i , j + 1 ) (i,j+1) (i,j+1) ( i − 1 , j ) (i-1,j) (i1,j)至少有一个必败点,从而推出 ( i − 1 , j + 1 ) (i-1,j+1) (i1,j+1)为必胜点。
那么我们只需要不断的往左上走,这样可以使得我们的NP值不变,最后在考虑该点左边点和上面点的NP值,这个NP值显然只跟格子的奇偶性有关,然后就可以得出该点的NP值了。

#include<bits/stdc++.h>
using namespace std;

const int N=100010;
int a[N],n;

int main(){	
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	sort(a+1,a+1+n);
	int now=n;
	while(now && n-now+2<=a[now-1]) now--;
	bool A=true,B=(((a[now]-(n-now+1))&1)^1);
	a[now]=n-now+1;
	for(int i=now-1;i>=1;i--) if(a[i]==a[i+1]) A^=true;
	else break;
	A&=B;
	printf(A?"Second":"First");
}

F - Leftmost Ball

对于一个合法序列,考虑一种 1 1 1 n n n的一种置换,出来肯定也是合法的。
设第一个 i i i出现的位置是 p i p_i pi,第 i i i 0 0 0出现的位置是 q i q_i qi
那么显然有: p i < p i + 1 , q i < p i , q i < q i + 1 p_i<p_{i+1},q_i<p_i,q_i<q_{i+1} pi<pi+1,qi<pi,qi<qi+1
考虑设置状态 f [ i ] [ j ] f[i][j] f[i][j]表示当前还剩下 i i i个0和后 j j j个数的方案数。
f [ i ] [ j ] = f [ i − 1 ] [ j ] + f [ i ] [ j − 1 ] ∗ C i + j ∗ ( k − 1 ) − 1 k − 2 f[i][j]=f[i-1][j]+f[i][j-1]*C_{i+j*(k-1)-1}^{k-2} f[i][j]=f[i1][j]+f[i][j1]Ci+j(k1)1k2
前面是考虑再放一个 0 0 0,后面是考虑先放一个 n − j + 1 n-j+1 nj+1,后面再按组合数分配一下位置(因为后面这些数就没有位置限制了)。
这个过程需要保证 i ≤ j i\leq j ij,那么就做完了。

#include<bits/stdc++.h>
using namespace std;

const int N=2010,mod=1000000007;
int f[N][N],fac[4000010],inv[4000010],n,k;

void ad(int&x,int y){x=(x+y>=mod)?(x+y-mod):(x+y);}

int ksm(int x,int t){
	int tot=1;
	while(t){
		if(t&1) tot=1ll*tot*x%mod;
		x=1ll*x*x%mod;
		t/=2;
	}
	return tot;
}

int C(int x,int y){return 1ll*fac[x]*inv[y]%mod*inv[x-y]%mod;}

int main(){
	scanf("%d %d",&n,&k);
	if(n==1 || k==1) {printf("1\n");return 0;}
	fac[0]=1;for(int i=1;i<=4000000;i++) fac[i]=1ll*fac[i-1]*i%mod;
	inv[4000000]=ksm(fac[4000000],mod-2);for(int i=3999999;i>=0;i--) inv[i]=1ll*inv[i+1]*(i+1)%mod;
	f[0][0]=1;
	for(int i=0;i<=n;i++)
		for(int j=i;j<=n;j++){
			if(i && j>=i-1) ad(f[i][j],f[i-1][j]);
			if(j) f[i][j]=(f[i][j]+1ll*f[i][j-1]*C(i+(k-1)*j-1,k-2))%mod;
		}
	printf("%d\n",1ll*f[n][n]*fac[n]%mod);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值