• AtCoder Grand Contest 005


    AtCoder Grand Contest 005

    A - STring

    翻译

    给定一个只包含(ST)的字符串,如果出现了连续的(ST),就把他删去,然后所有位置前移。问最后剩下的串长。

    题解

    模拟栈,和维护括号一样的。

    #include<iostream>
    #include<cstring>
    using namespace std;
    #define MAX 200200
    char ch[MAX];
    int ans,tot;
    int main()
    {
    	cin>>(ch+1);
    	for(int i=1,l=strlen(ch+1);i<=l;++i)
    		if(ch[i]=='S')++tot;
    		else tot?--tot:++ans;
    	cout<<ans+tot<<endl;
    	return 0;
    }
    

    B - Minimum Sum

    翻译

    给定排列(a_i)

    (sum_{l=1}^nsum_{r=l}^nmin(a_l,a_{l+1},...,a_r))

    题解

    比较套路的题目,对于每个数维护左右区间第一个比它大的数字,那么当前的这部分区间的最小值都是这个值,直接算贡献即可。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define ll long long
    #define MAX 200200
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    int n,a[MAX],Q[MAX],top;
    int l[MAX],r[MAX];
    ll ans;
    int main()
    {
    	n=read();
    	for(int i=1;i<=n;++i)a[i]=read(),l[i]=1,r[i]=n;
    	top=0;
    	for(int i=1;i<=n;++i)
    	{
    		while(top&&a[Q[top]]>a[i])--top;
    		if(top)l[i]=Q[top]+1;
    		Q[++top]=i;
    	}
    	top=0;
    	for(int i=n;i>=1;--i)
    	{
    		while(top&&a[Q[top]]>a[i])--top;
    		if(top)r[i]=Q[top]-1;
    		Q[++top]=i;
    	}
    	for(int i=1;i<=n;++i)ans+=1ll*(i-l[i]+1)*(r[i]-i+1)*a[i];
    	cout<<ans<<endl;
    	return 0;
    }
    

    C - Tree Restoring

    翻译

    有一个(n)个节点的树,告诉你距离每个点的最大距离,问这样的树是否存在。

    题解

    一个性质,距离树上任意一点的最远点一定是直径的一个端点。那么先看看直径是否存在,然后看看剩下的点的距离是否都满足大于直径长度的一半,对于直径长度的奇偶性分开考虑一下。细节好烦啊。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define MAX 111
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    int n,a[MAX],mx,vis[MAX];
    void Out(){puts("Impossible");exit(0);}
    int main()
    {
    	n=read();
    	for(int i=1;i<=n;++i)mx=max(mx,a[i]=read());
    	for(int i=1;i<=n;++i)vis[a[i]]++;
    	if(mx&1)
    	{
    		for(int i=mx-mx/2;i<=mx;++i)if((vis[i]-=2)<0)Out();
    		for(int i=mx-mx/2;i;--i)if(vis[i])Out();
    	}
    	else
    	{
    		if(!vis[mx/2])Out();vis[mx/2]-=1;
    		for(int i=mx/2+1;i<=mx;++i)if((vis[i]-=2)<0)Out();
    		for(int i=mx/2;i;--i)if(vis[i])Out();
    	}
    	puts("Possible");
    	return 0;
    }
    

    D - ~K Perm Counting

    翻译

    给定(n,K)。求(1..n)所有排列中,不存在(|a_i-i|=K)的排列个数。

    题解

    看到题目就可以往容斥的方面靠。求解至少有(K)个不合法的方案数,往(dp)方面靠。

    发现所有可能出现上述情况的数,一定都是模(K)意义下相等的数,那么把所有这样的数给扣下来,显然就变成了给你一个两侧都是(m)个点的二分图,第(i)个点向着(i-1)(i+1)连边,求连了(x)条边的方案数。这个直接简单(dp)算,状压一下下面的相邻的两个点的状态转移就好饿了。然后再把所有的结果全部合并,显然合并的过程互不影响,就是一个背包。

    这样求出来的是恰好匹配的方案数,但是剩下的数可以随便放,所以乘阶乘,从恰好变成了至少,那么容斥计算答案即可。

    相当好的一道题目。

    #include<iostream>
    #include<cstdio>
    using namespace std;
    #define ll long long
    #define MOD 924844033
    #define MAX 2020
    void add(int &x,int y){x+=y;if(x>=MOD)x-=MOD;}
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    int g[MAX][MAX][4];
    int f[MAX],jc[MAX];
    int n,K,m,ans;
    int main()
    {
    	n=read();K=read();m=(n-1)/K+1;
    	jc[0]=1;
    	for(int i=1;i<=n;++i)jc[i]=1ll*jc[i-1]*i%MOD;
    	g[1][0][0]=1;g[1][1][2]=1;
    	for(int i=1;i<m;++i)
    		for(int j=0;j<=i;++j)
    			for(int k=0;k<4;++k)
    			{
    				if(!g[i][j][k])continue;
    				add(g[i+1][j][k>>1],g[i][j][k]);
    				if(!(k&1))add(g[i+1][j+1][k>>1],g[i][j][k]);
    				add(g[i+1][j+1][(k>>1)|2],g[i][j][k]);
    			}
    	f[0]=1;int s=0;
    	for(int i=1;i<=K;++i)
    	{
    		int p=(n-i)/K+1;s+=p;
    		for(int j=s;j;--j)
    			for(int k=1;k<=p;++k)
    				if(j>=k)add(f[j],1ll*f[j-k]*((g[p][k][0]+g[p][k][1])%MOD)%MOD);
    	}
    	for(int i=0;i<=n;++i)f[i]=1ll*f[i]*jc[n-i]%MOD;
    	for(int i=0;i<=n;++i)add(ans,(i&1)?MOD-f[i]:f[i]);
    	printf("%d
    ",ans);
    	return 0;
    }
    

    E - Sugigma: The Showdown

    翻译

    有一个(n)个点的图,有(n-1)条红边,(n-1)条蓝边,每种颜色的边都连成了一棵树。(A)控制一个棋子从(X)开始,(B)控制一个棋子从(Y)开始。每次(A)可以不动或者走一条红边,(B)可以不动或者走一条蓝边。如果棋子相遇则游戏结束。(A)希望最大化游戏时间,(B)希望最小化游戏时间,求出游戏进行的时间,如果可以无限循环下去输出(-1)

    题解

    我只感觉自己有些想法,但是细节什么的有点难考虑啊。(还是题解好)

    先考虑如何判断游戏会永远进行下去。只要不出现被抓住的情况就可以进行下去啊。那么什么情况下会被抓住呢?假设当前(A)的位置是(u)(B)的位置是(v)。如果(u)以及使用红边下与其相邻的所有点在蓝边的情况下都与(v)的距离不超过(1),那么就原地等死吧。否则,反过来,如果出现了一条边((u,v)),满足(dis\_blue(u,v)gt 2),并且(A)已经到了(u)位置而且(B)还没抓住,那么只要(B)一靠近,(A)只需要沿着这条边往另外一个方向走,(B)至少要走两步才能过来,那么(A)接着换方向就好了,那么这样就停不下来了。

    前面既然用了"否则"这个词,意味着如果不能永远进行下去,那么(A)一定会被抓(这不废话吗)。那么显然不会走到任何一条满足在蓝树上两点距离大于(2)的边,深思熟虑一波,发现如果不存在这样的边的话,(A)打死都走不出(B)的子树。那么以(Y)为根节点,找到所有(A)能够到达的点,显然(A)只会前往一个深度最深的点然后在那里等死。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    #include<queue>
    using namespace std;
    #define ll long long
    #define MAX 200200
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    struct Line{int v,next,col;}e[MAX<<2];
    int h[MAX],cnt=1,E[MAX][2];
    inline void Add(int u,int v,int col){e[cnt]=(Line){v,h[u],col};h[u]=cnt++;}
    int dfn[MAX],tim,fa[MAX],dep[MAX],low[MAX],dis[MAX],X,Y,n;
    bool inf[MAX],vis[MAX];
    void dfs(int u,int ff)
    {
    	dfn[u]=++tim;fa[u]=ff;dep[u]=dep[ff]+1;
    	for(int i=h[u];i;i=e[i].next)
    		if(e[i].col&&e[i].v!=ff)
    			dfs(e[i].v,u);
    	low[u]=++tim;
    }
    bool check(int x,int y)
    {
    	if(dfn[x]>dfn[y])swap(x,y);
    	if(low[x]>=low[y])return dep[y]-dep[x]>2;
    	if(fa[x]==fa[y])return false;
    	return true;
    }
    void BFS()
    {
    	queue<int> Q;vis[X]=true;Q.push(X);
    	while(!Q.empty())
    	{
    		int u=Q.front();Q.pop();
    		for(int i=h[u];i;i=e[i].next)
    			if(!e[i].col&&!vis[e[i].v])
    			{
    				dis[e[i].v]=dis[u]+1;
    				if(dis[e[i].v]<dep[e[i].v])
    					vis[e[i].v]=true,Q.push(e[i].v);
    			}
    	}
    }
    int main()
    {
    	n=read();X=read();Y=read();
    	for(int i=1;i<n;++i)E[i][0]=read(),E[i][1]=read();
    	for(int i=1;i<n;++i)
    	{
    		int u=read(),v=read();
    		Add(u,v,1);Add(v,u,1);
    	}
    	dep[0]=-1;dfs(Y,0);
    	for(int i=1;i<n;++i)
    	{
    		int u=E[i][0],v=E[i][1];
    		if(check(u,v))inf[u]=inf[v]=true;
    		else Add(u,v,0),Add(v,u,0);
    	}
    	BFS();
    	for(int i=1;i<=n;++i)if(vis[i]&&inf[i]){puts("-1");return 0;}
    	int ans=0;
    	for(int i=1;i<=n;++i)if(vis[i])ans=max(ans,dep[i]<<1);
    	printf("%d
    ",ans);
    	return 0;
    }
    

    F - Many Easy Problems

    题面

    给定一个(n)个节点的树,定义(f(S))表示在树上包含点集(S)的最小联通块的大小。对于每一个(k),求出所有大小为(k)的点集的(f(S))的和。模数(924844033),原根是(5)

    题解

    总是觉得自己好(naive)啊,碰到题目总是毫无思路怎么办啊?我还有救吗?

    我们对于每一个点分开考虑他在什么时候会对于一个点集产生贡献。显然这样子是不好算的,还是正难则反,我们考虑它在什么时候不会对于一个点集产生贡献,假设点集大小为(K)。那么当且仅当选定的(K)个点都在以当前点为根时的同一子树内。所以当前点对于点集大小为(K)时,产生的贡献是(C_n^k-sum_{vin son} C_{size[v]}^k),其中(size)是子树大小。

    那么对于每一个(K),我们把所有点产生的贡献给加起来,也就是(nC_n^k-sum_{i=0}^n num[i]*C_i^k),其中(num[i])表示大小为(i)的子树的个数。发现(num)其实很好求,(dfs)一遍就完事了。前面那部分是定值,最后再考虑。后面的式子直接把组合数给拆开,显然是卷积的形式,(NTT)即可。

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<vector>
    using namespace std;
    #define MOD 924844033
    #define ll long long
    #define MAX 888888
    inline int read()
    {
    	int x=0;bool t=false;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=true,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return t?-x:x;
    }
    struct Line{int v,next;}e[MAX];
    int h[MAX],cnt=1;
    inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
    int n,f[MAX],g[MAX],size[MAX];
    int jc[MAX],jv[MAX],inv[MAX];
    void dfs(int u,int ff)
    {
    	size[u]=1;
    	for(int i=h[u];i;i=e[i].next)
    	{
    		int v=e[i].v;if(v==ff)continue;
    		dfs(v,u);size[u]+=size[v];
    		++f[size[v]];
    	}
    	++f[n-size[u]];
    }
    int C(int n,int m){if(m>n)return 0;return 1ll*jc[n]*jv[m]%MOD*jv[n-m]%MOD;}
    int N,l,r[MAX],W[MAX];
    int fpow(int a,int b)
    {
    	int s=1;
    	while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}
    	return s;
    }
    void NTT(int *P,int opt)
    {
    	for(int i=1;i<N;++i)if(i>r[i])swap(P[i],P[r[i]]);
    	for(int i=1;i<N;i<<=1)
    	{
    		int w=fpow(5,(MOD-1)/(i<<1));W[0]=1;
    		for(int k=1;k<i;++k)W[k]=1ll*W[k-1]*w%MOD;
    		for(int p=i<<1,j=0;j<N;j+=p)
    			for(int k=0;k<i;++k)
    			{
    				int X=P[j+k],Y=1ll*P[i+j+k]*W[k]%MOD;
    				P[j+k]=(X+Y)%MOD;P[i+j+k]=(X+MOD-Y)%MOD;
    			}
    	}
    	if(opt==-1)
    	{
    		reverse(&P[1],&P[N]);
    		for(int i=0,inv=fpow(N,MOD-2);i<N;++i)P[i]=1ll*P[i]*inv%MOD;
    	}
    }
    int main()
    {
    	n=read();
    	for(int i=1;i<n;++i)
    	{
    		int u=read(),v=read();
    		Add(u,v);Add(v,u);
    	}
    	dfs(1,0);
    	jc[0]=jv[0]=inv[0]=inv[1]=1;
    	for(int i=1;i<=n;++i)jc[i]=1ll*jc[i-1]*i%MOD;
    	for(int i=2;i<=n;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
    	for(int i=1;i<=n;++i)jv[i]=1ll*jv[i-1]*inv[i]%MOD;
    	for(N=1;N<n+n;N<<=1)++l;
    	for(int i=0;i<N;++i)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
    	for(int i=0;i<=n;++i)f[i]=1ll*f[i]*jc[i]%MOD;
    	for(int i=0;i<=n;++i)g[i]=jv[n-i];
    	NTT(f,1);NTT(g,1);
    	for(int i=0;i<N;++i)f[i]=1ll*f[i]*g[i]%MOD;
    	NTT(f,-1);
    	for(int i=1;i<=n;++i)f[i]=1ll*f[i+n]*jv[i]%MOD;
    	for(int i=1;i<=n;++i)f[i]=(1ll*n*C(n,i)%MOD+MOD-f[i])%MOD;
    	for(int i=1;i<=n;++i)printf("%d
    ",f[i]);
    	return 0;
    }
    
  • 相关阅读:
    Python 的with关键字
    java解析xml
    Java IO & Serialization
    Java动态编译
    爬虫下载City Scape数据
    Pytorch多GPU训练
    可视化利器Visdom
    GLOG使用Demo
    hyperopt自动调参
    [Redis源码阅读]redis持久化
  • 原文地址:https://www.cnblogs.com/cjyyb/p/9675563.html
Copyright © 2020-2023  润新知