zoukankan      html  css  js  c++  java
  • CF1439D INOI Final Contests

    一、题目

    点此看题

    二、解法

    给的 (O(n^3)) 的数据范围,可以考虑计数 (dp)

    一种显然的思路是依次考虑人的位置,比如我们考虑最后一个人的最终位置,但是初始位置到最终位置之间是坐满了人的,剩下左右两边又要重新分配人,划分子问题太复杂了,有点难做。

    但是我们可以反过来考虑格子,设 (f[i][j]) 表示 (i) 个格子 (j) 的人的总贡献(人有编号顺序),(g[i][j]) 表示总方案数,当 (i>j) 时一定会有一个格子空出来,而且考虑边缘的格子是限制最小的,所以可以枚举最后一个空格子,这个空格子是不可越过的,所以就有了两个子问题,用组合数把人划分到两边去即可:

    [g[i][j]=g[i-1][j]+sum_{k>0} g[i-k-1][j-k]cdot g[k][k]cdot {jchoose k} ]

    [f[i][j]=f[i-1][j]+sum_{k>0}(g[i-k-1][j-k]cdot f[k][k]+f[i-k-1][j-k]cdot g[k][k])cdot {jchoose k} ]

    但是 (i=j) 的情况要另当别论,我们考虑最后一个人的最终位置是 (j),因为没有空格我们只需要保证其他人不会越过 (j) 即可,显然我们得到了两个子问题,设 (s(n)=sum_{i=1}^n i),转移:

    [g[i][i]=(i+1)cdot sum_{j=1}^ig[j-1][j-1]cdot g[i-j][i-j]cdot{i-1choose j-1} ]

    [f[i][i]leftarrow (s(j-1)+s(i-j))cdot g[j-1][j-1]cdot g[i-j][i-j]cdot {i-1choose j-1} ]

    [f[i][i]leftarrow (f[j-1][j-1]cdot g[i-j][i-j]+f[i-j][i-j]cdot g[j-1][j-1])cdot (i+1)cdot{i-1choose j-1} ]

    最后一个人起点和选方向的方案数是 (i+1)

    三、总结

    这道题技巧性还是很强的,(dp) 的时候选取考虑对象时不要局限;转移的时候注意子问题的划分,比如本题的转移就是利用题目的限制设置分界线,使得两个子问题通过该分界线分开;最后是转移的时候选限制最小的方式转移。

    最后解释一下为什么本题不适用等概率环模型,我现在就只见到了两种适用情况:

    • 题目可以修改描述方式使得符合该模型,可以轻易的算出合法的方案数。
    • 如果容易算全局序列的方案数,但是单点的方案数算不出来,可以主动转成环,通过讨论得到环的方案数,由于每个单点等概率的性质,可以把总方案数等概率分配到每个单点上。

    这种题是很灵活的,所以还是不要乱转,这道题转了好像真的没什么用。

    #include <cstdio>
    #define int long long
    const int M = 505;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,p,f[M][M],g[M][M],C[M][M];
    void init()
    {
    	C[0][0]=1;
    	for(int i=1;i<=n;i++)
    	{
    		C[i][0]=1;
    		for(int j=1;j<=i;j++)
    			C[i][j]=(C[i-1][j-1]+C[i-1][j])%p;
    	}
    }
    int sum(int x)
    {
    	return x*(x+1)/2%p;
    }
    signed main()
    {
    	n=read();m=read();p=read();
    	init();g[0][0]=1;
    	for(int i=1;i<=n;i++)
    	{
    		for(int j=1;j<=i;j++)
    		{
    			int t=g[j-1][j-1]*g[i-j][i-j]%p*C[i-1][j-1]%p;
    			int s=(f[j-1][j-1]*g[i-j][i-j]+f[i-j][i-j]*g[j-1][j-1])%p;
    			g[i][i]=(g[i][i]+(i+1)*t)%p;
    			f[i][i]+=(sum(j-1)+sum(i-j))*t%p;
    			f[i][i]+=(i+1)*C[i-1][j-1]%p*s%p;
    			f[i][i]%=p;
    		}
    	}
    	for(int i=1;i<=n;i++)
    		for(int j=0;j<i;j++)//start from 0
    		{
    			f[i][j]=f[i-1][j],g[i][j]=g[i-1][j];
    			for(int k=1;k<=j;k++)
    			{
    				g[i][j]=(g[i][j]+g[k][k]*g[i-k-1][j-k]%p*C[j][k])%p;
    				f[i][j]+=(f[i-k-1][j-k]*g[k][k]+f[k][k]*g[i-k-1][j-k])%p*C[j][k]%p;
    				f[i][j]%=p;
    			}
    		}
    	printf("%lld
    ",f[n][m]);
    }
    
  • 相关阅读:
    Xpath在选择器中正确,在代码中返回的是空列表问题
    爬虫爬取实例与乱码的处理
    计算机知识
    Django的学习(六)————templates过滤器、Django shell、admin
    Django的学习(五)————实战问题
    Django的学习(四)———— admin
    Django的学习(三)————models
    Window10 MySQL5.7.X 使用SSL连接
    asp.net 防止 跨站脚本
    实用的Windows 工具命令
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/14992391.html
Copyright © 2011-2022 走看看