• 8.2 NOI模拟赛 Palindrome dp 组合计数 分类讨论


    LINK:Palindrome

    avatar
    avatar

    写过的最ex的dp 题目了 对细节要求极高的组合计数问题.

    爆搜很容易写.

    考虑第二个subtask 每个数字都最多只有两个

    那么我们(dp_{i,j})表示前i种数字单个使用了j个的方案数.

    合理转移即可.

    下面几个subtask很没意思 几乎和正解的分类程度差不多深 所以这里不再分析.

    考虑正解:

    容易观察出来出现次数为2的数字是最特殊的 因为它能无限套.

    其他的出现次数只有三种形式:1 单个出现 2 自己和自己套 3 自己和其他的东西套.

    其中1 3都可以外套次数2的东西.

    而次数二的多一种就是 套别人/自己的形式.

    那么我们考虑如何dp 出方案 我们可以把次数二外套别人单独考虑.

    这样我们只需要dp出有多少个回文串即可.

    而还需要知道到底有多少个可以被外套多少个不能被外套 还需要知道2的剩余数量 还要记上一次想套下一个的数量.

    这里 我们考虑怎么dp合适:排列和数量为i的种类的本质不同问题.

    如果两个都在dp中不考虑 那么最后乘以相应的阶乘之后会出错.

    分析 考虑本质不同:我们需要在转移的时候乘以组合数类似的问题.

    而排列 我们需要像插空法那样做。

    考虑排列会困难一点 两个都考虑也不必要。

    只考虑让他们本质不同。这样最后得到的排列就会合法.

    细细分析每一个转移:

    自己单独 这个可以直接乘以组合数没什么好说的.

    自己套自己 需要预处理出(s_i)表示(2cdot i)个相互配对的方案数 递推得到(s_i=s_{i-1}cdot (2cdot i-1))

    自己套别人 乘以组合数 还要乘以一个阶乘 因为这也是一个配对问题。

    自己留下 乘以组合数.

    重头戏是计算答案.

    先要乘以排列的阶乘 然后考虑用数量为2的往上套.

    考虑对一个回文串套了k个 要乘以组合数还要乘以阶乘(谁先套上去的意思.

    这个东西可以再写一个dp 给dp出来.

    我的做法是 仔细观察 其实是有n个回文串要分m个次数为2的套.

    且回文串不同 套也不同 可以不被分到.

    我们把本质不同看成本质相同 就是一个隔板法的问题.

    或者可以暴力枚举分了几段每段分了多少 然后是一个组合数乘以阶乘的形式可以化简 化简之后也可以发现是一个隔板法.

    说到这里这道题就讲完了.

    很多东西都是在debug的时候才想到的 还是在想的时候就没有认真思考 导致浪费过多的时间.

    code
    //#include<bits/stdc++.h>
    #include<iostream>
    #include<cstdio>
    #include<ctime>
    #include<cctype>
    #include<queue>
    #include<deque>
    #include<stack>
    #include<iostream>
    #include<iomanip>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<ctime>
    #include<cmath>
    #include<cctype>
    #include<cstdlib>
    #include<queue>
    #include<deque>
    #include<stack>
    #include<vector>
    #include<algorithm>
    #include<utility>
    #include<bitset>
    #include<set>
    #include<map>
    #define ll long long
    #define db double
    #define INF 1000000000
    #define inf 1000000000000000ll
    #define ldb long double
    #define pb push_back
    #define put_(x) printf("%d ",x);
    #define get(x) x=read()
    #define gt(x) scanf("%d",&x)
    #define gi(x) scanf("%lf",&x)
    #define put(x) printf("%d
    ",x)
    #define putl(x) printf("%lld
    ",x)
    #define rep(p,n,i) for(RE int i=p;i<=n;++i)
    #define go(x) for(int i=lin[x],tn=ver[i];i;tn=ver[i=nex[i]])
    #define fep(n,p,i) for(RE int i=n;i>=p;--i)
    #define vep(p,n,i) for(RE int i=p;i<n;++i)
    #define pii pair<int,int>
    #define mk make_pair
    #define RE register
    #define P 13331ll
    #define gf(x) scanf("%lf",&x)
    #define pf(x) ((x)*(x))
    #define uint unsigned long long
    #define ui unsigned
    #define EPS 1e-5
    #define sq sqrt
    #define S second
    #define F first
    #define mod 1000000007
    #define md 998244353
    #define max(x,y) ((x)<(y)?y:x)
    #define l(i) t[i].l
    #define r(i) t[i].r
    #define mx(i) t[i].mx
    #define w(i) t[i].w
    #define zz p<<1
    #define yy p<<1|1
    using namespace std;
    char buf[1<<15],*fs,*ft;
    inline char getc()
    {
    	return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;
    }
    inline int read()
    {
    	RE int x=0,f=1;RE char ch=getc();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    	return x*f;
    }
    inline ll Read()
    {
    	RE ll x=0,f=1;RE char ch=getc();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();}
    	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();}
    	return x*f;
    }
    const int MAXN=6410,maxn=82;
    int n,m=80,ans,maxx;
    int a[MAXN],b[MAXN];
    int fac[MAXN],inv[MAXN],mi[MAXN];
    
    inline int ksm(int b,int p)
    {
    	int cnt=1;
    	while(p)
    	{
    		if(p&1)cnt=(ll)cnt*b%mod;
    		b=(ll)b*b%mod;p=p>>1;
    	}
    	return cnt;
    }
    
    inline int C(int a,int b)
    {
    	return a<b?0:fac[a]*(ll)inv[b]%mod*inv[a-b]%mod;
    }
    
    inline void add(int &x,int y){x=x+y>=mod?x+y-mod:x+y;}
    
    int f[2][maxn][maxn][maxn][maxn],s[maxn];
    
    int main()
    {
    	//freopen("1.in","r",stdin);
    	//freopen("1.out","w",stdout);
    	
    	get(n);maxx=m;
    	
    	rep(1,n,i)++a[read()];
    	
    	rep(1,m,i)++b[a[i]];
    	
    	fac[0]=1;mi[0]=1;
    	
    	rep(1,maxx,i)fac[i]=(ll)fac[i-1]*i%mod,mi[i]=mi[i-1]*2%mod;
    	
    	inv[maxx]=ksm(fac[maxx],mod-2);
    	
    	fep(maxx-1,0,i)inv[i]=(ll)inv[i+1]*(i+1)%mod;
    	
    	rep(1,m,i)ans+=C(a[i]+1,2);
    	
    	printf("%d ",ans);
    	
    	ans=0;
    	
    	s[0]=1;
    	
    	rep(1,m,i)s[i]=s[i-1]*(ll)(2*i-1)%mod;
    		
    	f[0][0][0][0][0]=1;int ss=0,u=0;
    	
    	rep(1,m,W)
    	{
    		
    		u=u^1;
    		
    		rep(0,ss,i)rep(0,ss,j)rep(0,b[W-1],k)rep(0,b[2],l)f[u][i][j][k][l]=0;
    		
    		rep(0,ss,i)rep(0,ss,j)rep(0,min(b[W],b[W-1]),k)rep(0,b[2],l)
    		
    		{
    					
    			if(!f[u^1][i][j][k][l])continue;
    					
    			int res=b[W]-k;
    			
    			if(W==2)
    			{
    				if(k)continue;
    				rep(0,res/2,r)
    				{
    					int rres=res-r*2;
    					rep(0,min(b[W+1],rres),v)
    					
    					{
    		
    						int rrres=rres-v;
    						rep(0,rrres,vv)
    			
    							add(f[u][i+v+vv][j+r][v][rrres-vv],(ll)f[u^1][i][j][k][l]*C(b[W],k)%mod*C(res,2*r)%mod*C(rres,v)%mod*C(rrres,vv)%mod*mi[r]%mod*s[r]%mod*fac[v]%mod);
    					}
    				
    				}
    			}
    			else 
    			{
    				int lim=W==1?0:res/2;
    				
    				rep(0,lim,r)
    			
    				{
    			
    					int rres=res-r*2;			
    					rep(0,min(b[W+1],rres),v)
    					{
    					
    						add(f[u][i+rres][j+r][v][l],(ll)f[u^1][i][j][k][l]*C(b[W],k)%mod*C(res,2*r)%mod*C(rres,v)%mod*mi[r]%mod*s[r]%mod*fac[v]%mod);
    					}
    				}
    			}	
    		
    		}
    		ss+=b[W];
    	}
    	rep(0,ss,i)rep(0,ss/2,j)rep(0,b[2],l)
    	{
    		if(!f[u][i][j][0][l])continue;
    		int cnt=f[u][i][j][0][l];
    		if(i)cnt=(ll)cnt*fac[i+j]%mod*C(i+l-1,i-1)%mod*fac[l]%mod;
    		else
    		{
    			if(l)cnt=0;
    			else cnt=(ll)cnt*fac[j]%mod;
    		}
    		add(ans,cnt);
    		//cout<<i<<' '<<j<<' '<<0<<' '<<l<<' '<<f[u][i][j][0][l]<<' '<<cnt<<endl;
    		
    	}
    	
    	put(ans);
    	
    	//put(ww);
    	
    	return 0;
    
    }
    
  • 相关阅读:
    PLSQL13
    01.Spring引入
    验证码重构
    短信验证码登录思路
    记住我 token保存到数据库
    图形验证码及其重构
    个性化用户认证流程
    01.Spring Security初识,表单认证
    堆和栈的区别
    系统分析与设计第二次作业
  • 原文地址:https://www.cnblogs.com/chdy/p/13430444.html
Copyright © 2020-2023  润新知