Atcoder AGC002 题解

版权声明:这篇文章的作者是个蒟蒻,没有转载价值,如果要转说一下好了 https://blog.csdn.net/litble/article/details/83078289

A - Range Product

如果区间包含0就输出Zero,如果负数的个数是奇数个就输出Negative,否则输出Positive

#include<bits/stdc++.h>
using namespace std;
#define RI register int
int a,b;
int main()
{
	scanf("%d%d",&a,&b);
	if(a<=0&&b>=0) puts("Zero");
	else if(a<0&&b<0&&(b-a+1)%2) puts("Negative");
	else puts("Positive");
	return 0;
}

B - Box and Ball

直接模拟。对于从x盒子里拿一个球到y盒子这一步,若x盒子里可能有红球,则y盒子里也有可能有红球了。但是若把x盒子拿空了,x盒子就不可能有红球了。

#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
	int q=0;char ch=' ';
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
	return q;
}
const int N=100005;
int red[N],num[N],n,m,ans;
int main()
{
	int x,y;
	n=read(),m=read();
	for(RI i=1;i<=n;++i) num[i]=1;
	red[1]=1;
	for(RI i=1;i<=m;++i) {
		x=read(),y=read();
		if(red[x]) red[y]=1;
		--num[x],++num[y];
		if(!num[x]) red[x]=0;
	}
	for(RI i=1;i<=n;++i) if(red[i]) ++ans;
	printf("%d\n",ans);
	return 0;
}

C - Knot Puzzle

考虑进行到只剩一个绳结,则两段绳子的长度和必须大于等于L,否则这个绳结没法解。所以找到整根绳子中长度和大于等于L的两段,将左边的绳结从左往右解开,右边的绳结从右往左解开,最后将这个绳结解开。

#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
	int q=0;char ch=' ';
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
	return q;
}
const int N=100005;
int n,L,a[N];
void work(int x) {
	puts("Possible");
	for(RI i=1;i<x;++i) printf("%d\n",i);
	for(RI i=n-1;i>x;--i) printf("%d\n",i);
	printf("%d\n",x);
}
int main()
{
	n=read(),L=read();
	for(RI i=1;i<=n;++i) a[i]=read();
	for(RI i=1;i<n;++i)
		if(a[i]+a[i+1]>=L) {work(i);return 0;}
	puts("Impossible");
	return 0;
}

D - Stamp Rally

整体二分+按秩合并资兹删除操作的并查集。

#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
	int q=0;char ch=' ';
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
	return q;
}
const int N=100005;
struct edge{int x,y;}e[N];
struct quesion{int x,y,z,id;}q[N],k1[N],k2[N];
int n,m,Q,top;
int ans[N],f[N],sz[N],d[N],s1[N],s2[N],tag[N];
int find(int x) {while(x!=f[x]) x=f[x];return x;}
void work(int l,int r,int ql,int qr) {
	if(ql>qr) return;
	if(l==r) {
		for(RI i=ql;i<=qr;++i) ans[q[i].id]=l;
		return;
	}
	int mid=(l+r)>>1,ktop=top,js1=0,js2=0;
	for(RI i=l;i<=mid;++i) {
		int r1=find(e[i].x),r2=find(e[i].y);
		if(r1!=r2) {
			if(d[r1]<d[r2]) swap(r1,r2);
			++top,s1[top]=r1,s2[top]=r2;
			f[r2]=r1,sz[r1]+=sz[r2];
			if(d[r1]==d[r2]) ++d[r1],tag[top]=1;
			else tag[top]=0;
		}
	}
	for(RI i=ql;i<=qr;++i) {
		int r1=find(q[i].x),r2=find(q[i].y),cango;
		if(r1==r2) cango=sz[r1];
		else cango=sz[r1]+sz[r2];
		if(cango>=q[i].z) k1[++js1]=q[i];
		else k2[++js2]=q[i];
	}
	for(RI i=1;i<=js1;++i) q[ql+i-1]=k1[i];
	for(RI i=1;i<=js2;++i) q[ql+js1-1+i]=k2[i];
	work(mid+1,r,ql+js1,qr);
	while(top>ktop) {
		f[s2[top]]=s2[top],sz[s1[top]]-=sz[s2[top]];
		if(tag[top]) --d[s1[top]];
		--top;
	}
	work(l,mid,ql,ql+js1-1);
}
int main()
{
	n=read(),m=read();
	for(RI i=1;i<=m;++i) e[i].x=read(),e[i].y=read();
	Q=read();
	for(RI i=1;i<=Q;++i)
		q[i].x=read(),q[i].y=read(),q[i].z=read(),q[i].id=i;
	for(RI i=1;i<=n;++i) f[i]=i,d[i]=sz[i]=1;
	work(1,m,1,Q);
	for(RI i=1;i<=Q;++i) printf("%d\n",ans[i]);
	return 0;
}

E - Candy Piles

把糖果从大到小排序,然后看做一个不规则的棋盘,第 i i 列有 a i a_i 个格子。从左下角的格子出发,每个人的操作是可以向上走一格或向右走一格,走到上面和右边都没有格子的地方就输了。

按博弈的思想,有两种状态,必胜态N和必败态P。所有上面和右边都没有格子的格子状态是P,其他格子(x,y)的状态和(x+1,y+1)状态相同。

这是因为,能走到P的格子状态是N,只能走到N的状态是P。如果(x+1,y+1)是P,则(x,y+1)和(x+1,y)都是N,则(x,y)必是P;如果(x+1,y+1)是N,则(x+1,y+2)和(x+2,y+1)中有一个是P,则(x,y+2)和(x+2,y)中有一个是N,则(x,y+1)和(x+1,y)中有一个是P,则(x,y)是N。

那么我们只要找到一个最大的没越界的格子(x,x),看一下它的上面和右边的状态,算出它的状态,它的状态就是(1,1)的状态。

#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
	int q=0;char ch=' ';
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
	return q;
}
const int N=100005;
int n,a[N];
int cmp(int x,int y) {return x>y;}
int main()
{
	n=read();
	for(RI i=1;i<=n;++i) a[i]=read();
	sort(a+1,a+1+n,cmp);
	for(RI i=1;i<=n;++i)
		if(i+1>a[i+1]) {
			int j=i+1;while(a[j]==i) ++j;
			int ans=(j-i-1)&1;
			ans|=(a[i]-i)&1;
			if(ans) puts("First");
			else puts("Second");
			return 0;
		}
	return 0;
}

F - Leftmost Ball

序列的任意一个后缀,第 n + 1 n+1 种颜色的球的数量都不能超过其他颜色的颜色总数。

我们发现前 n n 种颜色都是等价的,所以先钦定一个颜色加入的顺序,最后再去乘一个 n ! n!

f ( i , j ) f(i,j) 表示放了 i i 种(除第 n + 1 n+1 种以外的)颜色, j j 个第 n + 1 n+1 种颜色的球的序列方案数。考虑在这个序列的最前面放一个第 n + 1 n+1 种颜色的球还是放一个新颜色的球,再把剩下的新颜色球插入到后面,这样进行转移。

#include<bits/stdc++.h>
using namespace std;
#define RI register int
const int mod=1000000007,N=2005,M=4000005;
int n,K,f[N][N],fac[M],ni[M];
int qm(int x) {return x>=mod?x-mod:x;}
int ksm(int x,int y) {
	int re=1;
	for(;y;y>>=1,x=1LL*x*x%mod) if(y&1) re=1LL*re*x%mod;
	return re;
}
void prework() {
	fac[0]=1;for(RI i=1;i<=n*K;++i) fac[i]=1LL*fac[i-1]*i%mod;
	ni[n*K]=ksm(fac[n*K],mod-2);
	for(RI i=n*K-1;i>=0;--i) ni[i]=1LL*ni[i+1]*(i+1)%mod;
}
int C(int d,int u) {return 1LL*fac[d]*ni[u]%mod*ni[d-u]%mod;}
int main()
{
	scanf("%d%d",&n,&K);
	if(K==1) {puts("1");return 0;}
	prework();--K;
	f[0][0]=1;
	for(RI i=1;i<=n;++i) {
		for(RI j=0;j<=i;++j) {
			if(j) f[i][j]=f[i][j-1];
			f[i][j]=qm(f[i][j]+1LL*f[i-1][j]*C(i*K+j-1,K-1)%mod);
		}
	}
	printf("%lld\n",1LL*f[n][n]*fac[n]%mod);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/litble/article/details/83078289
今日推荐