- (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;
}