AtCoder Grand Contest 005题解

传送门

\(A\)

咕咕

const int N=5e5+5;
char s[N];int res,n,sum;
int main(){
	scanf("%s",s+1),res=n=strlen(s+1);
	fp(i,1,n)if(s[i]=='S')++sum;
		else if(sum)--sum,res-=2;
	printf("%d\n",res);
	return 0;
}

\(B\)

咕咕

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
using namespace std;
typedef long long ll;
const int N=2e5+5,inf=0x3f3f3f3f;
int a[N],l[N],r[N],st[N],n,top;ll res;
int main(){
	scanf("%d",&n);
	fp(i,1,n)scanf("%d",&a[i]);
	a[0]=a[n+1]=0,st[++top]=0;
	fp(i,1,n){
		while(top&&a[i]<a[st[top]])--top;
		l[i]=st[top],st[++top]=i;
	}
	st[top=1]=n+1;
	fd(i,n,1){
		while(top&&a[i]<a[st[top]])--top;
		r[i]=st[top],st[++top]=i;
	}
	fp(i,1,n)res+=1ll*a[i]*(i-l[i])*(r[i]-i);
	printf("%lld\n",res);
	return 0;
}

\(C\)

因为树上所有直径有同一个中点或同一条中边,那么\(dis\)最小的点只能有\(1\)个或者两个

然后我们从中点开始构造一条直径,如果无法构造出无解,否则剩下的点都可以挂在直径上而满足条件

最后判一下构造出的直径是否满足它给出的\(dis\)即可

const int N=105;
int c[N],n,mn=233,mx;
int main(){
	scanf("%d",&n);
	for(R int i=1,x;i<=n;++i)scanf("%d",&x),++c[x],cmin(mn,x),cmax(mx,x);
	if(c[mn]>2)return puts("Impossible"),0;
	fp(i,mn+1,mx)if(c[i]<2)return puts("Impossible"),0;
	if(c[mn]==2&&mn!=mx-mn+1)return puts("Impossible"),0;
	if(c[mn]==1&&mn!=mx-mn)return puts("Impossible"),0;
	return puts("Possible"),0;
}

\(D\)

这种题目显然是容斥了

然而如果直接枚举有几个点选了不该选的数会有问题,因为有的点选不该选的数有两种方案,有的点选了别的点就不能选

发现选了之后\(i\)只会影响\(...,i-4k,i-2k,i+2k,i+4k,...\)的数的选法,那么我们把所有模\(2k\)意义下相等的位置一起考虑,记\(f_{i,j,0/1/2}\)表示考虑到这一类中的第\(i\)个数,选了其中\(j\)个,第\(i\)个数不选\(/\)选择在这个位置放\(i-k/\)选择在这个位置放\(i+k\)的方案数,转移即可,最后暴力加到全局的背包里,即可计算出\(s[i]\)表示至少有这\(i\)个位置选择不合法的方案数,就可以直接套容斥了

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
using namespace std;
const int P=924844033;
inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
    R int res=1;
    for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
    return res;
}
const int N=2005;
int n,k,res,len,f[N][3],g[N][3],s[N],t[N],fac[N];
void calc(R int x){
	memset(f,0,sizeof(f));
	f[0][0]=1;
	R int p=0;
	for(;x<=n;x+=(k<<1)){
		fp(i,0,p){
			g[i][0]=f[i][0],g[i][1]=f[i][1],g[i][2]=f[i][2];
			f[i][0]=f[i][1]=f[i][2]=0;
		}
		fp(i,0,p){
			R int s=add(g[i][0],add(g[i][1],g[i][2]));
			upd(f[i][0],s);
			if(x>k)upd(f[i+1][1],dec(s,g[i][2]));
			if(x+k<=n)upd(f[i+1][2],s);
		}
		++p;
	}
	memset(t,0,sizeof(t));
	fp(i,0,len)fp(j,0,p)upd(t[i+j],mul(s[i],add(f[j][0],add(f[j][1],f[j][2]))));
	len+=p;
	fp(i,0,len)s[i]=t[i];
}
int main(){
//	freopen("testdata.in","r",stdin);
	scanf("%d%d",&n,&k);
	fac[0]=1;fp(i,1,n)fac[i]=mul(fac[i-1],i);
	s[0]=1;
	fp(i,1,min(k<<1,n))calc(i);
	fp(i,0,len)upd(res,i&1?dec(0,mul(fac[n-i],s[i])):mul(fac[n-i],s[i]));
	printf("%d\n",res);
	return 0;
}

\(E\)

博弈论菜鸡在此

首先如果存在一个情况,\(A\)\(u\)\(B\)\(v\),如果\(u\)能走到的任何一条边在蓝树上到\(v\)的距离都小于等于\(1\)\(A\)只能等死了,否则如果存在一条边\((u,v)\)满足蓝树上的距离大于\(2\),那么\(A\)就可以一直利用这条边绕\(B\)了,此时输出\(-1\)

如果会被追到,那么以蓝树上的\(Y\)为根,我们发现\(A\)根本走不出这棵子树,那么我们找到所有\(A\)能到达的点,然后到其中深度最大的点等死就行了

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(head,u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
using namespace std;
const int N=1e6+5;
struct eg{int v,nx;}e[N<<1];int bl[N],rd[N],tot;
inline void add(R int *head,R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
int fr[N],to[N],dep[N],fa[N],dfn[N],low[N],ok[N],vis[N],dis[N],tim,n,S,T;
void dfs(int *head,int u){
	dfn[u]=++tim;
	go(head,u)if(v!=fa[u])fa[v]=u,dep[v]=dep[u]+1,dfs(head,v);
	low[u]=tim;
}
inline bool ck(R int u,R int v){
	if(dfn[u]>dfn[v])swap(u,v);if(low[u]>=low[v])return dep[v]-dep[u]>2;
	if(fa[u]==fa[v])return false;return true;
}
int q[N];
void bfs(){
	int h=1,t=0,u;q[++t]=S,vis[S]=1;
	while(h<=t){
		u=q[h++];
		go(rd,u)if(!vis[v]){
			dis[v]=dis[u]+1;
			if(dis[v]<dep[v])vis[v]=1,q[++t]=v;
		}
	}
}
int main(){
	scanf("%d%d%d",&n,&S,&T);
	for(R int i=1,u,v;i<n;++i)scanf("%d%d",&u,&v),fr[i]=u,to[i]=v;
	for(R int i=1,u,v;i<n;++i)scanf("%d%d",&u,&v),add(bl,u,v),add(bl,v,u);
	dfs(bl,T);
	for(R int i=1,u,v;i<n;++i){
		u=fr[i],v=to[i];
		if(ck(u,v))ok[u]=ok[v]=1;
		else add(rd,u,v),add(rd,v,u);
	}
	bfs();
	fp(i,1,n)if(vis[i]&&ok[i])return puts("-1"),0;
	R int res=0;
	fp(i,1,n)if(vis[i])cmax(res,dep[i]<<1);
	printf("%d\n",res);
	return 0;
}

\(F\)

先考虑如何计算单个\(k\),那么对于每个点分别计算贡献,一个点会被计入贡献的次数是\({n\choose k}-\sum_{v\in son_u}{sz_v\choose k}\),其中\(sz_v\)表示以\(u\)为根时\(v\)子树的大小

那么\(n\)个点加起来就是\(n\times {n\choose k}-\sum_{i=1}^n cnt[i]\times {i\choose k}\),把组合数拆成阶乘的形式就可以把后面看成一个卷积了,\(NTT\)即可

//quming
#include<bits/stdc++.h>
#define R register
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
using namespace std;
const int P=924844033,gi=554906420;
inline void upd(R int &x,R int y){(x+=y)>=P?x-=P:0;}
inline int add(R int x,R int y){return x+y>=P?x+y-P:x+y;}
inline int dec(R int x,R int y){return x-y<0?x-y+P:x-y;}
inline int mul(R int x,R int y){return 1ll*x*y-1ll*x*y/P*P;}
int ksm(R int x,R int y){
	R int res=1;
	for(;y;y>>=1,x=mul(x,x))(y&1)?res=mul(res,x):0;
	return res;
}
const int N=(1<<19)+5;
struct eg{int v,nx;}e[N<<1];int head[N],tot;
inline void Add(R int u,R int v){e[++tot]={v,head[u]},head[u]=tot;}
int fac[N],ifac[N],r[25][N],rt[2][N],f[N],g[N],inv[25];
int n,lim,d;
inline void swap(R int &x,R int &y){R int t=x;x=y,y=t;}
inline int C(R int n,R int m){return m>n?0:1ll*fac[n]*ifac[m]%P*ifac[n-m]%P;}
void init(){
	fac[0]=ifac[0]=1;fp(i,1,262144)fac[i]=mul(fac[i-1],i);
	ifac[262144]=ksm(fac[262144],P-2);fd(i,262143,1)ifac[i]=mul(ifac[i+1],i+1);
    fp(d,1,19){
        fp(i,1,(1<<d)-1)r[d][i]=(r[d][i>>1]>>1)|((i&1)<<(d-1));
        inv[d]=ksm(1<<d,P-2);
    }
    for(R int t=(P-1)>>1,i=1,x,y;i<=262144;i<<=1,t>>=1){
        x=ksm(5,t),y=ksm(gi,t),rt[0][i]=rt[1][i]=1;
        fp(k,1,i-1)
            rt[1][i+k]=mul(rt[1][i+k-1],x),
            rt[0][i+k]=mul(rt[0][i+k-1],y);
    }
}
void NTT(int *A,int ty){
	fp(i,0,lim-1)if(i<r[d][i])swap(A[i],A[r[d][i]]);
	R int t;
	for(R int mid=1;mid<lim;mid<<=1)
		for(R int j=0;j<lim;j+=(mid<<1))
			fp(k,0,mid-1)
				A[j+k+mid]=dec(A[j+k],t=mul(rt[ty][mid+k],A[j+k+mid])),
				A[j+k]=add(A[j+k],t);
	if(!ty){
		t=inv[d];
		fp(i,0,lim-1)A[i]=mul(A[i],t);
	}
}
int sz[N];
void dfs(int u,int fa){
	sz[u]=1;
	go(u)if(v!=fa){
		dfs(v,u),sz[u]+=sz[v];
		++f[n-sz[v]],++f[sz[v]];
	}
}
int main(){
//	freopen("testdata.in","r",stdin);
	scanf("%d",&n);
	for(R int i=1,u,v;i<n;++i)scanf("%d%d",&u,&v),Add(u,v),Add(v,u);
	dfs(1,0);init();
	lim=1,d=0;
	while(lim<=(n<<1))lim<<=1,++d;
	fp(i,0,n)f[i]=mul(f[i],fac[i]),g[i]=ifac[n-i];
	NTT(f,1),NTT(g,1);
	fp(i,0,lim-1)f[i]=mul(f[i],g[i]);
	NTT(f,0);
	fp(i,1,n)printf("%d\n",dec(mul(n,C(n,i)),mul(f[i+n],ifac[i])));
	return 0;
}
posted @ 2019-08-16 08:21  源曲明  阅读(207)  评论(0编辑  收藏  举报