BZOJ #3746: [POI2015]Czarnoksiężnicy okrągłego stołu 動態規劃

  轉載請註明出處:http://www.cnblogs.com/TSHugh/p/8823423.htmlhtml

  讀完題就會發現p=0、1的狀況以及n=一、2的狀況均可以直接判掉,而p=2的時候也能夠直接構造,那麼如今須要的就是當p=3且n>=3的時候的作法.
  容易想到小數據範圍下的dfs,可是這難以優化,因而去思考dp的作法.個人思路一開始是dp弧,後來發現能夠直接dp兩個鏈,可是複雜度太大,並不比dfs優秀多少.去看題解,只有Claris寫了題解,他是這樣寫的:ide

p=3時不考慮1的座位進行DP
能夠發現對於i+1的位置安排,咱們只關心i-2,i-1,i的相對順序以及它們的相鄰、邊界狀況
因此設f[i][j][S1][S2]表示已經安排了前i我的的座位,i-2,i-1,i的順序爲j,是否有人在兩端點爲S1,是否有人相鄰爲S2的方案數 
答案最後再除以n
這樣複雜度有點飛…
優化

  這並無使我滿意,由於我感受這在時間、空間、代碼各方面的複雜度都是不優秀的.
  此時我看到了金策的700+ms作法,並且代碼也並不長,這讓我意識到此題有更加優秀的作法,因而在搜尋標程失敗後去poi官網get了一發題解,而後利用google翻譯了一發,獲得了一個神奇的作法.
  首先,題解裏說了一句話,這題其實是在數哈密頓迴路,我思考了一下,好像是這樣的……然而,這題的解法和哈密頓迴路並無什麼卵關係……
  轉化一下問題:google

I.把全部編號i變爲n-i.
II.把環拆開,把原問題變成——求一段序列知足題目限制,且開頭必定爲0,結尾必定爲1/2/3.spa

  這樣的話,再對三種結尾判斷一下取捨,就能獲得最終答案了.
  對於如今的問題能夠設計dp狀態(好神奇的狀態啊……):翻譯

f[i]:對於一段序列,開頭爲i,結尾爲i+1,且序列中的數字均屬於[i,n),此序列知足題目限制的方案數.
g[i]:對於一段序列,開頭爲i+1,結尾爲i,且序列中的數字均屬於[i,n),此序列知足題目限制的方案數.
設計

  先看dp的轉移(好厲害的轉移啊……):code

先貼一張圖(來自波蘭題解):htm

你看這張圖,你就會懂得求解方法了,因而獲得了一個遞推式:f[i]=g[i+1]+g[i+2]+g[i+4]+g[i+5].
同理,也能夠獲得:g[i]=f[i+1]+f[i+2]+f[i+4]+f[i+5].
可是,上述方法彷佛只適用於i<=n-8,因此,對於i>=n-7,咱們就能夠直接dfs處理了.
(注意判斷額外限制條件)
blog

  假設三種結尾的方案數分別爲ans一、ans二、ans3.
  既然知道了dp的轉移,那麼怎麼算三種ans呢?
  沿用剛纔轉移的思路,能夠獲得(圖仍然來自波蘭題解):

ans1=f[0];(顯然)

ans2=f[1]+f[3]+f[4];(緣由見上圖)

ans3=f[2]+f[4]+f[5]+g[3]+g[4];(緣由見上圖)
(注意判斷額外限制條件)

  因此對於p=3且n>=3的時候,判斷一下,若n<=7,直接dfs,不然使用上述方法.
  至此,這道題就解決了,時間複雜度爲O(n),實現見代碼:

#include <cstdio>
#include <cstring>
#include <algorithm>
char xB[(1<<15)+10],*xS=xB,*xT=xB;
#define gtc (xS==xT&&(xT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xT)?0:*xS++)
inline void read(int &x){
  register char ch=gtc;
  for(x=0;ch<'0'||ch>'9';ch=gtc);
  for(;ch>='0'&&ch<='9';x=(x<<1)+(x<<3)+ch-'0',ch=gtc);
}
const int N=1000010;
const int Inf=0x3f3f3f3f;
const int P=1000000007;
int n,k,p,f[N],g[N];
bool NO[N][7],die[N],vf[N],vg[N];
#define no(a,b) (NO[a][(b)+3])
#define ok(a,b) (!no(a,(b)-(a)))
inline int work(){
  int i,x,y,ans=0;
  for(i=1;i<=k;++i){
    read(x),read(y);
    if(std::abs(x-y)<=3)no(x,y-x)=true;
  }
  for(i=1;i<=n;++i)
    if(i&1)f[(i+1)>>1]=i;
    else f[n-(i>>1)+1]=i;
  f[0]=f[n],f[n+1]=f[1];
  ++ans;
  for(i=1;i<=n;++i)
    if(no(f[i],f[i+1]-f[i])){
      --ans;break;
    }
  ++ans;
  for(i=1;i<=n;++i)
    if(no(f[i],f[i-1]-f[i])){
      --ans;break;
    }
  printf("%d\n",ans);
  return 0;
}
inline int dfs(int pos,int last,int k,int t,int len){
  if(pos==len)return std::abs(t-last)<=3&&ok(last,t);
  int i,ret=0;
  for(i=std::max(last-3,k+1);i<=last+3&&i<n;++i)
    if((!die[i])&&ok(last,i)){
      die[i]=true;
      ret+=dfs(pos+1,i,k,t,len);
      die[i]=false;
    }
  return ret;
}
inline int D(int s,int k,int t){
  die[s]=die[t]=true;
  int ret=dfs(2,s,k,t,n-k);
  die[s]=die[t]=false;
  return ret;
}
inline int F(int x);
inline int G(int x);
inline int F(int x){
  if(vf[x])return f[x];
  vf[x]=true;
  if(n-x<=7)return f[x]=D(x,x,x+1);
  int ret=0;
  if(ok(0+x,2+x))ret=(ret+G(1+x))%P;
  if(ok(0+x,3+x)&&ok(2+x,1+x))ret=(ret+G(2+x))%P;
  if(ok(0+x,3+x)&&ok(3+x,2+x)&&ok(2+x,5+x)&&ok(4+x,1+x))ret=(ret+G(4+x))%P;
  if(ok(0+x,3+x)&&ok(3+x,6+x)&&ok(5+x,2+x)&&ok(2+x,4+x)&&ok(4+x,1+x))ret=(ret+G(5+x))%P;
  return f[x]=ret;
}
inline int G(int x){
  if(vg[x])return g[x];
  vg[x]=true;
  if(n-x<=7)return g[x]=D(x+1,x,x);
  int ret=0;
  if(ok(2+x,0+x))ret=(ret+F(1+x))%P;
  if(ok(1+x,2+x)&&ok(3+x,0+x))ret=(ret+F(2+x))%P;
  if(ok(1+x,4+x)&&ok(5+x,2+x)&&ok(2+x,3+x)&&ok(3+x,0+x))ret=(ret+F(4+x))%P;
  if(ok(1+x,4+x)&&ok(4+x,2+x)&&ok(2+x,5+x)&&ok(6+x,3+x)&&ok(3+x,0+x))ret=(ret+F(5+x))%P;
  return g[x]=ret;
}
inline int Work(){
  int i,x,y,d=100000,ans=0;
  for(i=1;i<=k;++i){
    read(x),read(y);
    x=n-x,y=n-y;
    if(std::abs(x-y)<=3)no(x,y-x)=true;
  }
  if(n<=7){
    if(ok(1,0))ans=(ans+D(0,0,1))%P;
    if(ok(2,0))ans=(ans+D(0,0,2))%P;
    if(n>=4&&ok(3,0))ans=(ans+D(0,0,3))%P;
    printf("%d\n",ans);
    return 0;
  }
  for(i=n-d;i>d;i-=d)G(i),F(i);/*爲了防止爆棧和MLE*/
  if(ok(1,0))ans=(ans+F(0))%P;
  if(ok(2,0)&&ok(0,1))ans=(ans+F(1))%P;
  if(ok(4,1)&&ok(1,2)&&ok(2,0)&&ok(0,3))ans=(ans+F(3))%P;
  if(ok(5,2)&&ok(2,0)&&ok(0,3)&&ok(3,1)&&ok(1,4))ans=(ans+F(4))%P;
  if(ok(3,0)&&ok(0,1)&&ok(1,2))ans=(ans+F(2))%P;
  if(ok(5,2)&&ok(2,3)&&ok(3,0)&&ok(0,1)&&ok(1,4))ans=(ans+F(4))%P;
  if(ok(6,3)&&ok(3,0)&&ok(0,1)&&ok(1,4)&&ok(4,2)&&ok(2,5))ans=(ans+F(5))%P;
  if(ok(3,0)&&ok(0,2)&&ok(2,1)&&ok(1,4))ans=(ans+G(3))%P;
  if(ok(4,1)&&ok(1,3)&&ok(3,0)&&ok(0,2)&&ok(2,5))ans=(ans+G(4))%P;
  printf("%d\n",ans);
  return 0;
}
int main(){
  //freopen("cza.in","r",stdin);
  //freopen("cza.out","w",stdout);
  read(n),read(k),read(p);
  if(n==1)return puts("1"),0;
  if(p==0)return puts("0"),0;
  if(n==2)return puts(k?"0":"1"),0;
  if(p==1)return puts("0"),0;
  if(p==2)return work(),0;
  return Work(),0;
}
Kod
相關文章
相關標籤/搜索