• CSP-S2019 题解


    T1 格雷码

    考虑第(i)位,第(0)位分别为(011001100dots),第(1)位分别为(001111000011110000dots),从而第(i)位等于(koplusleftlfloordfrac k2 ight floor)的第(i)位。使用unsigned long long存贮,按位输出即可。

    unsigned long long n,k;
    int res[65],i;
    
    int main()
    {
    	cin>>n>>k;
    	k=k^(k>>1ull);
    	
    	while(k)
    	{
    		res[i++]=k&1ull;
    		k>>=1ull;
    	}
    	
    	for(int i=n-1;~i;--i)
    		printf("%d",res[i]);
    
    	puts("");
    
            return 0;
    }
    

    T2 括号树

    先考虑链的情况。我们设(s_i)为以第(i)位为结尾的合法括号串的数量,维护一个栈(S),当第(i)位为(时入栈,当第(i)位为)时匹配栈顶(t)(若栈为空则直接令(s_i=0)),该位的(s)值就等于匹配到的栈顶(t)的前一位的(s)值加一,即(s_i=s_{t-1}+1),同时弹出栈顶。最终的答案(mathrm{ans}_i)就等于(s_i)的前缀和。

    进一步地,一棵树可以拆成由根到叶子的若干条链。我们按照链的方式即可计算(s_i)(mathrm{ans}_i),只不过每个节点(i)的前一个节点变成了(mathrm{fa}_i),dfs时注意将栈回溯即可。

    const int Maxn=5e5+7;
    
    typedef long long LL;
    
    char str[Maxn];
    int n,stac[Maxn],top,fa[Maxn];
    LL f[Maxn],ans[Maxn];
    
    struct Edge
    {
    	int nxt,to;
    }e[Maxn];
    
    int edge_cnt,head[Maxn];
    
    inline void add_edge(int u,int v)
    {
    	e[++edge_cnt].nxt=head[u];
    	e[edge_cnt].to=v;
    	head[u]=edge_cnt;
    }
    
    inline void DFS(int u)
    {
    	int tmp=0;
    	
    	if(str[u]==')')
    	{
    		if(top)
    		{
    			tmp=stac[top];
    			f[u]=f[fa[tmp]]+1;
    			--top;
    		}
    	}
    	else if(str[u]=='(')
    		stac[++top]=u;
    	
    	ans[u]=ans[fa[u]]+f[u];
    	
    	for(int i=head[u];i;i=e[i].nxt)
    		DFS(e[i].to);
    	
    	if(tmp)
    		stac[++top]=tmp;
    	else if(top)
    		--top;
    }
    
    int main()
    {
    	scanf("%d",&n);
    	scanf("%s",str+1);
    	
    	for(int i=2,f;i<=n;++i)
    		scanf("%d",&f),
    		fa[i]=f,
    		add_edge(f,i);
    	
    	DFS(1);
    	
    	LL res=0;
    	
    	for(int i=1;i<=n;++i)
    		res^=(1LL*i*ans[i]);
    	
    	printf("%lld
    ",res);
    	
    	return 0;
    }
    

    T4 Emiya家今天的饭

    先考虑(mle 3)的部分分。设(f(i,A,B,C))表示处理到前(i)行,三种食材分别选了(A,B,C)个时的方案数。则

    [f(i,A,B,C)leftarrow f(i-1,A,B,C)+a_{i1}cdot f(i-1,A-1,B,C)+a_{i2}cdot f(i-1,A,B-1,C)+a_{i3}cdot f(i-1,A,B,C-1) ]

    边界为(f(0,0,0,0)=1)。最后的答案为

    [mathrm{ans}=sum_{A,B,Cle n}f(n,A,B,C),qquad max{A,B,C}ledfrac{A+B+C}2, A+B+C>0 ]

    可以倒序枚举(A,B,C)来压掉(i)这一维。总复杂度(O(n^4))

    (mge3)时,(m)迅速增大,无法使用(O(n^{m+1}))的做法。考虑容斥,合法方案数就等于总方案数减去不合法方案数。

    我们设(f(i,j))表示处理到前(i)行,选了(j)种食材的总方案数。则

    [f(i,j)leftarrow f(i-1,j)+s_icdot f(i-1,j-1) ]

    其中(s_i)为第(i)行的前缀和,边界为(f(0,0)=1)。总方案数为

    [S=sum_{j=1}^n f(n,j) ]

    可以倒序枚举(j)来压掉(i)这一维。这部分的复杂度(O(nm))

    注意到,不合法的方案中有且仅有一种食材(c)的出现次数大于总菜数的一半。(O(m))枚举(c),对于给定的(c),设(g_c(i,j,k))为处理到前(i)行,其中第(c)种食材选了(j)种方式,其余食材选了(k)种方式时的方案数,则

    [g_c(i,j,k)leftarrow g_c(i-1,j,k)+a_{ic}cdot g_c(i-1,j-1,k)+(s_i-a_{ic})cdot g_c(i-1,j,k-1) ]

    边界为(g_c(0,0,0)=1)。总不合法的方案数即为

    [S'=sum_{c=1}^msum_{j>k}g_c(n,j,k) ]

    同理可以倒序枚举(j,k)来压掉(i)这一维,最终答案即为(mathrm{ans}=S-S')。这部分的复杂度(O(n^2m)),总复杂度(O(n^2m))

    注意到我们并不在意(j,k)的具体值,可用它们的差值(delta=j-k)来描述状态,则(g_c)的转移方程变为

    [g_c(i,delta)leftarrow g_c(i-1,delta)+a_{ic}cdot g_c(i-1,delta-1)+(s_i-a_{ic})cdot g_c(i-1,delta+1) ]

    边界为(g_c(0,0)=1)。总不合法的方案数即为

    [S'=sum_{c=1}^msum_{delta>0}g_c(n,delta) ]

    总复杂度降为(O(nm))。可以将(delta)加上(n)来防止越界。

    const int Maxn=107;
    const int Maxm=2e3+7;
    
    const int Mod=998244353;
    
    typedef long long LL;
    
    LL f[Maxn],g[Maxn][Maxn<<1],ans;
    int n,m;
    LL a[Maxn][Maxm],s[Maxn];
    
    signed main()
    {
    	scanf("%d%d",&n,&m);
    	
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=m;++j)
    			scanf("%lld",&a[i][j]),
    			s[i]=(s[i]+a[i][j])%Mod;
    
    	f[0]=1;
    	
    	for(int i=1;i<=n;++i)
    		for(int j=i;j;--j)
    			f[j]=(f[j]+1LL*s[i]*f[j-1])%Mod;
    	
    	for(int j=1;j<=n;++j)
    		ans=(ans+f[j])%Mod;
    	
    	for(int c=1;c<=m;++c)
    	{
    		memset(g,0,sizeof(g));
    	
    		g[0][n]=1;
    		
    		for(int i=1;i<=n;++i)
    			for(int d=1;d<=n+i;++d)
    				g[i][d]=(g[i][d]+g[i-1][d]+1LL*g[i-1][d-1]*a[i][c]+1LL*g[i-1][d+1]*(s[i]-a[i][c]))%Mod;
    		
    		for(int d=n+1;d<=n*2;++d)
    				ans=(ans-g[n][d]+Mod)%Mod;
    	}
    	
    	printf("%lld
    ",ans);
    
            return 0;
    }
    
  • 相关阅读:
    JobScheduler调度器过程(JobSchedulerService的启动过程)
    Android 9 新功能 及 API 介绍(提供了实用的模块化的功能支持,包括 人工智能)
    好用的在线工具汇总:Iconfont图标,数据mock,时间函数库,颜色查询 等
    前端编码规范小记
    android自定义控件 几种方式总结
    App开发如何利用Fidder,在api接口还没有实现的情况下模拟数据,继续开发
    WebView一般用法总结
    360等杀掉了app的主进程后 ,如何自动开启 如何防止被kill
    android内存优化
    dp跟px的互相转换
  • 原文地址:https://www.cnblogs.com/Anverking/p/CSP-S.html
Copyright © 2020-2023  润新知