题目大意
给你一个有n*(n-1)/2个点的等边三角形,顶端的点为(1,1),对于第i行,一共有i个点(i,1..i),你要在这个三角形上画m条折线,每条线包含n个点。具体的,假如你目前折线端点画到(i,j),你可以选择画到(i+1,j)或(i+1,j+1)。
现在给你约束:
1,对于任意折线a,b,设X[a][i]表示折线a的第i个端点为(i,X[a][i]),若a小于b,则对于所有i=1~n,都有X[a][i]≤X[b][i]。
2,给出若干组(A,B,C),表示折线A在B-1到B的过程中只能走0(左边)或者1(右边)
解题思路
很容易搞出一个暴力DP,设f[i][s]表示处理了前i条,第i条的曲折装态为s,s某一位是0则代表向左走,1类似。
那么暴力转移是O(n4^n)的。
考虑优化。我们知道s能够转移到s’,必须保证s的每一个前缀和都小于s’,从这里入手。
设f[i][j][s]表示做到第i条的第j行,这一条的前j行和s的状态一样,并且在将来这一条折线不会在s所代表折线的左边。
转移很简单,枚举第j+1行走哪边。假如走左边,那么s的这一位必须是0;走右边,s的这一位如果是1直接走,如果不是就从后面找一个1把他移动到这里(没有就直接把这位改成1),转移即可。
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmax(a,b) (a=(a>b)?a:b)
#define cmin(a,b) (a=(a<b)?a:b)
typedef long long ll;
const int N=1e6+5,M=2e6+5,mo=1e9+7;
int f[2][(1<<20)+5],c[25][25],n,m,K,x,y,z,q1,q2,i,j,s,ts,ns,ds,ans;
int main()
{
freopen("t4.in","r",stdin);
//freopen("t4.out","w",stdout);
scanf("%d %d %d",&n,&m,&K);
n--;
fo(i,1,K)
{
scanf("%d %d %d",&x,&y,&z);
c[x][y-1]=z+1;
}
f[0][0]=1;
q1=0;
q2=1;
ts=1<<n;ts--;
fo(i,1,m)
fo(j,0,n-1)
{
fo(s,0,ts)
if (f[q1][s])
{
//left
if (c[i][j]!=2&&((s>>j)&1)==0)
{
f[q2][s]+=f[q1][s];
if (f[q2][s]>=mo) f[q2][s]-=mo;
}
//right
if (c[i][j]!=1)
{
if ((s>>j)&1) ns=s;
else
{
ns=((-1)^(1<<j+1)-1)&s;
if (!ns) ds=0;
else ds=ns&(-ns);
ns=s^(1<<j)^ds;
}
f[q2][ns]+=f[q1][s];
if (f[q2][ns]>=mo) f[q2][ns]-=mo;
}
}
fo(s,0,ts) f[q1][s]=0;
q1^=1;
q2^=1;
}
fo(s,0,ts)
{
ans+=f[q1][s];
if (ans>=mo) ans-=mo;
}
printf("%d\n",ans);
}