zoukankan      html  css  js  c++  java
  • 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;
    
    }
    
  • 相关阅读:
    C#深入浅出 修饰符(二)
    HDU 5785 Interesting
    HDU 5783 Divide the Sequence
    HDU 5781 ATM Mechine
    UVA 714 Copying Books
    uva 1471 Defense Lines
    UVA 11134 Fabled Rooks
    UVA 11572 Unique Snowflakes
    UVA 11093 Just Finish it up
    UVA 10954 Add All
  • 原文地址:https://www.cnblogs.com/chdy/p/13430444.html
Copyright © 2011-2022 走看看