長沙大佬的博客
Orz高一進隊,Orz pku一本,跟我這種人真是雲泥之別
今年二月我還不知道在哪個地方玩泥巴的時候人家都已經把插頭dp打得爐火純青了,Orz orz
大佬的博客寫得非常好了,我就隨便口胡兩句
一開始不是很懂這玩意,直到看到另一個大佬說的一句話
“值得注意的一點是,插頭不是表示將要去某處的虛擬狀態,而是表示已經到達某處的現實狀態。
也就是說,如果有一個插頭指向某個格子,那么這個格子已經和插頭來源聯通了,我們接下來要考慮的是從這個插頭往哪里走。”
任意條回路的情況,用輪廓線dp這個路徑,輪廓線上就是若干向右或向支出來的部分,這種東西就被取名叫插頭,用二進制狀態表示每單位輪廓線上有沒有這種支出來的插頭-也就是一條路徑經過它並且沒成回路下一步要繼續沿着支出的方向走。
如圖,紅色的是一條輪廓線,綠色代表插頭,插頭並不表示路徑的方向,僅表示路徑接下來應向那邊延伸(淺綠色虛線代表這個期望的延伸)
那么對於一個插頭的轉移看能往那邊走就往那邊轉,如圖,紫色代表轉移后的輪廓線,粉色代表左右綠色插頭的兩種轉移方式
對於兩個相鄰的插頭就直接連起來即可(如下圖)。最后的答案就是在最后一個能走的格子中兩個插頭這樣連起來的方案數(當然這時候這條輪廓線前面的那些綠色插頭都是不存在的)。
任意條回路的方案數就很容易的求完了。
如果只有一條回路,就不能隨便把兩個插頭連起來了(上一段加黑的部分)。因為兩個插頭代表的路徑可能已經聯通了,再相連的話就會提前形成一個回路(如下圖,上方灰色的條路徑使兩個插頭已經聯通,再把淺綠的相連就形成回路了)。
但如果是這樣的路徑連起來就是合法的,因為這兩個插頭一開始在灰色路徑中並沒有聯通
兩種常用的處理辦法:
1、最小表示法,我還沒寫過,不過NOI2007那道題構造轉移的時候也是用最小表示法表示聯通關系的,大概差不多吧
2、括號序列
這樣的聯通關系顯然是構成括號序列的,那么只需要把二進制變成三進制,每個插頭區分是左括號還是右括號就好了
轉移的時候,b1,b2代表綠箭頭指的兩條輪廓線上的插頭情況
只有一個插頭的情況連通性不改變,很好處理,主要需要注意這種轉移(其余請轉移參考最上面大佬的博客,主要是我懶得寫了。。。)
藍色的是原來的括號序列,當用淺綠色把兩個插頭連起來時,聯通性改變
括號序列就變成了這樣,也就是找到右邊對應的第一個右括號改成左括號即可,如下圖。和這種轉移對稱的找左括號的轉移同理。
Eat the Trees
真-最簡單的插頭dp入門題,因為不需要維護插頭的連通性,每條線上只需要記有無插頭,或許直接叫輪廓線dp比較合適。

1 //Achen
2 #include<algorithm>
3 #include<iostream>
4 #include<cstring>
5 #include<cstdlib>
6 #include<vector>
7 #include<cstdio>
8 #include<queue>
9 #include<cmath>
10 #include<set>
11 #include<map>
12 #define Formylove return 0
13 #define For(i,a,b) for(int i=(a);i<=(b);i++)
14 #define Rep(i,a,b) for(int i=(a);i>=(b);i--)
15 const int N=4107; 16 typedef long long LL; 17 typedef double db; 18 using namespace std; 19 int Cas,n,m,a[15][15],ex,ey; 20 LL ans,f[2][N]; 21
22 template<typename T>void read(T &x) { 23 char ch=getchar(); x=0; T f=1; 24 while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar(); 25 if(ch=='-') f=-1,ch=getchar(); 26 for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f; 27 } 28
29 LL solve() { 30 LL rs=0; 31 int up=(1<<(m+1))-1,o=0; 32 For(i,0,up) f[o][i]=0; 33 f[o][0]=1; 34 For(i,1,n) { 35 For(j,1,m) { 36 o^=1; 37 For(s,0,up) f[o][s]=0; 38 For(s,0,up) if(f[o^1][s]) { 39 int ls=j==1?(s<<1):s,b1=(ls&(1<<(j-1))),b2=(ls&(1<<j)); 40 if(!b1&&!b2&&!a[i][j]) f[o][ls]+=f[o^1][s]; 41 else if(!b1&&!b2) { 42 if(a[i+1][j]&&a[i][j+1]) 43 f[o][ls|(1<<(j-1))|(1<<j)]+=f[o^1][s]; 44 } 45 else if(!b1) { 46 if(a[i+1][j]) 47 f[o][(ls|(1<<(j-1)))^(1<<j)]+=f[o^1][s]; 48 if(a[i][j+1]) 49 f[o][ls]+=f[o^1][s]; 50 } 51 else if(!b2) { 52 if(a[i+1][j]) 53 f[o][ls]+=f[o^1][s]; 54 if(a[i][j+1]) 55 f[o][(ls^(1<<(j-1)))|(1<<j)]+=f[o^1][s]; 56 } 57 else { 58 if(a[i][j]) { 59 f[o][ls^(1<<(j-1))^(1<<j)]+=f[o^1][s]; 60 if(i==ex&&j==ey&&(ls^(1<<(j-1))^(1<<j))==0) { 61 rs+=f[o^1][s]; 62 } 63 } 64 } 65 } 66 } 67 } 68 return rs; 69 } 70
71 int main() { 72 #ifdef ANS 73 freopen(".in","r",stdin); 74 freopen(".out","w",stdout); 75 #endif
76 read(Cas); 77 For(cas,1,Cas) { 78 read(n); read(m); 79 For(i,1,n) For(j,1,m){ 80 read(a[i][j]); 81 if(a[i][j]) ex=i,ey=j; 82 } 83 ans=solve(); 84 printf("Case %d: There are %lld ways to eat the trees.\n",cas,ans); 85 } 86 Formylove; 87 }
Formula1
需要維護連通性的真-插頭dp. hasn的時候找到了沒有return而是break和把Rep寫成For調了半個小時。。。

1 //Achen
2 #include<algorithm>
3 #include<iostream>
4 #include<cstring>
5 #include<cstdlib>
6 #include<vector>
7 #include<cstdio>
8 #include<queue>
9 #include<cmath>
10 #include<set>
11 #include<map>
12 #define Formylove return 0
13 #define For(i,a,b) for(int i=(a);i<=(b);i++)
14 #define Rep(i,a,b) for(int i=(a);i>=(b);i--)
15 const int N=1594327,p=299983; 16 typedef long long LL; 17 typedef double db; 18 using namespace std; 19 int n,m,a[15][15],ex,ey,power[20]; 20 char mp[15]; 21
22 template<typename T>void read(T &x) { 23 char ch=getchar(); x=0; T f=1; 24 while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar(); 25 if(ch=='-') f=-1,ch=getchar(); 26 for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f; 27 } 28
29 LL f[2][N]; 30 int S[2][N]; 31
32 int o,fir[p+10],nxt[N],tot[2]; 33 void ins(int s,LL val) { 34 int tp=s%p+1; 35 for(int i=fir[tp];i;i=nxt[i]) if(S[o][i]==s) { 36 f[o][i]+=val; return; 37 } 38 nxt[++tot[o]]=fir[tp]; fir[tp]=tot[o]; 39 S[o][tot[o]]=s; f[o][tot[o]]=val; 40 } 41
42 //f 方案數 S hash前的狀態 (4進制)
43 LL solve() { 44 LL rs=0; 45 o=1; 46 S[o][++tot[o]]=0; 47 f[o][tot[o]]=1; 48 For(i,1,n) For(j,1,m) { 49 o^=1; 50 tot[o]=0; 51 memset(fir,0,sizeof(fir)); 52 For(zt,1,tot[o^1]) { 53 int s=j==1?(S[o^1][zt]<<2):S[o^1][zt]; 54 LL num=f[o^1][zt]; 55 int b1=(s>>(2*j-2))%4,b2=(s>>(2*j))%4; 56 if(!a[i][j]) { 57 if(!b1&&!b2) ins(s,num); 58 } 59 else if(!b1&&!b2) { 60 if(a[i+1][j]&&a[i][j+1]) ins(s+power[j-1]+power[j]*2,num); 61 } 62 else if(!b1) { 63 if(a[i+1][j]) ins(s+b2*power[j-1]-b2*power[j],num); 64 if(a[i][j+1]) ins(s,num); 65 } 66 else if(!b2) { 67 if(a[i][j+1]) ins(s-b1*power[j-1]+b1*power[j],num); 68 if(a[i+1][j]) ins(s,num); 69 } 70 else if(b1==1&&b2==1) { 71 int kl=1; 72 For(t,j+1,m) { 73 if((s>>(2*t))%4==1) kl++; 74 if((s>>(2*t))%4==2) kl--; 75 if(!kl) { 76 ins(s-power[j-1]-power[j]-power[t],num); 77 break; 78 } 79 } 80 } 81 else if(b1==2&&b2==2) { 82 int kl=1; 83 Rep(t,j-2,0) { 84 if((s>>(2*t))%4==2) kl++; 85 if((s>>(2*t))%4==1) kl--; 86 if(!kl) { 87 ins(s-2*power[j-1]-2*power[j]+power[t],num); 88 break; 89 } 90 } 91 } 92 else if(b1==2&&b2==1) ins(s-2*power[j-1]-power[j],num); 93 else if(b1==1&&b2==2&&i==ex&&j==ey) 94 rs+=num; 95 } 96 } 97 return rs; 98 } 99
100 int main() { 101 #ifdef ANS 102 freopen(".in","r",stdin); 103 freopen(".out","w",stdout); 104 #endif
105 read(n); read(m); 106 For(i,1,n) { 107 scanf("%s",mp+1); 108 For(j,1,m) if(mp[j]=='.') { 109 a[i][j]=1; 110 ex=i; ey=j; 111 } 112 } 113 power[0]=1; 114 For(i,1,15) power[i]=(power[i-1]<<2); 115 LL ans=solve(); 116 printf("%lld\n",ans); 117 Formylove; 118 }