• 【洛谷3581】[POI2015] CZA(DP)


    点此看题面

    • (n)个人围着圆桌坐成一圈,要求满足相邻两人编号差不超过(p),且存在(k)对矛盾关系形如(b_i)不能坐在(a_i)右边。
    • 假设第(n)个人位置固定,求排座位的方案数。
    • (nle10^6,kle10^5,0le ple 3)

    (nle2)(ple2)的特判

    显然(n=1)时答案为(1)(n=2)时若(pge1)(k=0)则答案为(1),否则答案为(0)

    否则,当(nge 3)时,若(ple1)显然无解。

    (p=2)时实际上只有两种顺序相反的放法:(n)的一侧放(n-2,n-4,...),另一侧放(n-1,n-3,...)。直接分别检验两种情况是否可行即可。

    于是接下来就只需要考虑(nge 3)(p=3)的情况了。

    插入式(DP)

    一种比较经典的(DP)模型。

    本题有一个性质,在一个合法方案中,最小数两侧的数一定相差不超过(p),即一个合法局面在删去最小数之后仍是合法局面

    又由于最小数(i)只有可能插在(i+1,i+2,i+3)三个数之间,方案数相对较少,因此我们不妨从大到小一个一个插入数进行(DP)

    即,设(f_{i,0/1/2,0/1/2,0/1/2})表示最小值为(i)(i)(i+1)不相邻/(i)(i+1)右/(i)(i+1)左,(i+1)(i+2)不相邻/(i+1)(i+2)右/(i+1)(i+2)左,(i+2)(i)不相邻/(i+2)(i)右/(i+2)(i)的方案数。

    初始假设已经放好了(n-2,n-1,n)

    转移时首先要判断(i+1,i+2)(i+3)之间是否存在不合法的位置关系,如果有必须断开,且(i)不能与(i+3)产生新的不合法关系,因为之后的数无法再插在(i+3)旁边了。而(i,i+1,i+2)三者间暂时有不合法的位置关系是允许的,因为之后可能被断开。

    如果没有强制要求,简单讨论一波转移即可。

    代码:(O(n3^p))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Rg register
    #define RI Rg int
    #define Cn const
    #define CI Cn int&
    #define I inline
    #define W while
    #define N 1000000
    #define X 1000000007
    #define Inc(x,y) ((x+=(y))>=X&&(x-=X))
    using namespace std;
    int n,k,p,s[N+5],w[N+5][7],f[2][3][3][3];
    namespace FastIO
    {
    	#define FS 100000
    	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
    	char oc,FI[FS],*FA=FI,*FB=FI;
    	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
    }using namespace FastIO;
    int main()
    {
    	RI i,j,x,y,z;for(read(n),read(k),read(p),i=1;i<=k;++i) read(x),read(y),abs(y-x)<=p&&(w[x][y-x+p]=1);
    	if(n==1) return puts("1"),0;if(n==2) return puts(p>=1&&!k?"1":"0"),0;if(p<=1) return puts("0"),0;//特判n≤2和p≤1
    	if(p==2)//特判n=2,只有两种顺序相反的方法
    	{
    		for(s[x=1]=n,i=n-1;i>=1;i-=2) s[++x]=i;for(i=n&1?1:2;i<n;i+=2) s[++x]=i;s[n+1]=s[1];
    		for(i=1;i<=n;++i) if(w[s[i]][s[i+1]-s[i]+p]) break;RI ans=i>n;reverse(s+2,s+n+1);
    		for(i=1;i<=n;++i) if(w[s[i]][s[i+1]-s[i]+p]) break;return printf("%d
    ",ans+(i>n)),0;
    	}
    	for(f[n&1][1][1][1]=f[n&1][2][2][2]=1,i=n-3;i;--i)//DP,初始放好n-2,n-1,n
    	{
    		for(x=0;x^3;++x) for(y=0;y^3;++y) for(z=0;z^3;++z) f[i&1][x][y][z]=0;//清空
    		for(x=0;x^3;++x) for(y=0;y^3;++y) for(z=0;z^3;++z) if(f[i&1^1][x][y][z])
    		{
    			if((y==1&&w[i+2][4]||y==2&&w[i+3][2])&&(z==1&&w[i+3][1]||z==2&&w[i+1][5])) continue;//如果i+1,i+2都和i+3有关系,无法全部断开
    			#define T1 !w[i][6]&&Inc(f[i&1][0][x][1],f[i&1^1][x][y][z])//插在i+2和i+3之间
    			#define T2 !w[i+3][0]&&Inc(f[i&1][0][x][2],f[i&1^1][x][y][z])//插在i+3和i+2之间
    			#define T3 !w[i+3][0]&&Inc(f[i&1][1][x][0],f[i&1^1][x][y][z])//插在i+3和i+1之间
    			#define T4 !w[i][6]&&Inc(f[i&1][2][x][0],f[i&1^1][x][y][z])//插在i+1和i+3之间
    			if(y==1&&w[i+2][4]) {T1;continue;}if(y==2&&w[i+3][2]) {T2;continue;}//i+1和i+3存在不合法关系
    			if(z==1&&w[i+3][1]) {T3;continue;}if(z==2&&w[i+1][5]) {T4;continue;}//i+2和i+3存在不合法关系
    			y==1&&T1,y==2&&T2,z==1&&T3,z==2&&T4;//先前四种转移
    			x==1&&Inc(f[i&1][2][0][2],f[i&1^1][x][y][z]),x==2&&Inc(f[i&1][1][0][1],f[i&1^1][x][y][z]);//插在i+1和i+2之间;插在i+2和i+1之间
    		}
    	}
    	RI ans=0;for(x=0;x^3;++x) for(y=0;y^3;++y) for(z=0;z^3;++z)//统计答案
    		x==1&&w[1][4]||x==2&&w[2][2]||y==1&&w[2][4]||y==2&&w[3][2]||z==1&&w[3][1]||z==2&&w[1][5]||Inc(ans,f[1][x][y][z]);//最终状态不能存在任何不合法关系
    	return printf("%d
    ",ans),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    4-17 文字图片绘制
    4-16 矩形圆形任意多边形绘制
    4-15 线段绘制
    4-14 图像特效小结
    4-13 油画特效
    4-12 颜色映射
    4-11 浮雕效果
    Linux文本截取命令cut​笔记
    45张令程序员泪流满面的趣图
    45张令程序员泪流满面的趣图
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3581.html
Copyright © 2020-2023  润新知