插头dp初探
问题描述
插头dp用于解决一类可基于图连通性递推的问题。用插头来表示轮廓线上的连通性,然后根据连通性与下一位结合讨论进行转移。
表示连通性的方法
<最小表示法> 与字符串循环最小表示不同,这种方法用于给轮廓线上的联通情况确定一个唯一对应的标号序列,做法是从左至右轮廓线扫描,每扫描到一个未标号的位置就新建一个标号,并将轮廓线以后与这一位联通的位置都标上此号,不被包含的点标号为0。举例本质相同的连通性\((3,3,2,1,3)\)和\((2,2,3,1,2)\)都会被标记为\(1,1,2,3,1\)。
<括号表示法> 用于解决路径(、回路)相关的连通性。做法是将轮廓线上方的回路链接到轮廓向上插头区别为左插头与右插头,逐格转移时讨论格子上边有左边的插头;求解任意路径问题时保留左右插头但不合并,并且引入“独立插头”表示只有一端链接到轮廓线路径的链接端;
<其它> 引入一些插头,然后直接转换为进制数表示的不清楚怎么分类的方法。
具体操作
设\(f[x,s]\)为考虑到位置\(x\) 轮廓线状态为\(s\)的解。转移是个费脑子的事情,按下不表。连通性转换为进制数时选择\(2^t\)作为进制数可以更快速的取出、修改轮廓线上某一位的值,但时需要把所有的状态扔进hash表里。
练习题 (7/7)
luogu5056 【模板】插头dp
我就不造轮子了 讲解可以参考ladylex 的例题2(虽然不是一道题,但分类讨论差不多的而且有图解)
#include <bits/stdc++.h>
#define LL long long
const int mod=299987;
int n,m,endx,endy;
LL ans;
char a[20][20];
struct hash_set {
LL val[mod];
int siz,key[mod],hsh[mod];
void clear() {
memset(val,0,sizeof val);
memset(key,-1,sizeof key);
memset(hsh,0,sizeof hsh);
siz=0;
}
void newhsh(int id,int vl) {
hsh[id]=++siz,key[siz]=vl;
}
LL&operator[](const int &sta) {
for(int i=sta%mod; ;i=(i+1==mod?0:i+1)) {
if(!hsh[i]) newhsh(i,sta);
if(key[hsh[i]]==sta) return val[hsh[i]];
}
}
} f[2];
int find(int sta,int id) {
return (sta>>((id-1)<<1))&3;
}
void set(int &sta,int bit,int val) {
bit=(bit-1)<<1;
sta|=3<<bit;
sta^=3<<bit;
sta|=val<<bit;
}
int link(int sta,int pos) {
int cnt=0,dlt=(find(sta,pos)==1?1:-1);
for(int i=pos; i&&i<=m+1; i+=dlt) {
int plg=find(sta,i);
if(plg==1) cnt++;
else if(plg==2) cnt--;
if(!cnt) return i;
}
return -1;
}
void p_dp(int x,int y) {
int now=((x-1)*m+y)&1,lst=now^1,tot=f[lst].siz;
f[now].clear();
for(int i=1; i<=tot; ++i) {
int sta=f[lst].key[i];
LL val=f[lst].val[i];
if(link(sta,y)==-1||link(sta,y+1)==-1)
continue; // 状态不可用
int p1=find(sta,y),p2=find(sta,y+1);
if(a[x][y]!='.') {
if(!p1&&!p2) f[now][sta]+=val;
} else if(!p1&&!p2) {
if(a[x+1][y]=='.'&&a[x][y+1]=='.') {
set(sta,y,1);
set(sta,y+1,2);
f[now][sta]+=val;
}
} else if(p1&&!p2) {
if(a[x+1][y]=='.') f[now][sta]+=val;
if(a[x][y+1]=='.') {
set(sta,y,0);
set(sta,y+1,p1);
f[now][sta]+=val;
}
} else if(!p1&&p2) {
if(a[x][y+1]=='.') f[now][sta]+=val;
if(a[x+1][y]=='.') {
set(sta,y,p2);
set(sta,y+1,0);
f[now][sta]+=val;
}
} else if(p1==1&&p2==1) { // '((' ))
set(sta,link(sta,y+1),1);
set(sta,y,0);
set(sta,y+1,0);
f[now][sta]+=val;
} else if(p1==1&&p2==2) { // '()'
if(x==endx&&y==endy) ans+=val;
} else if(p1==2&&p2==1) { // ')(' => merge
set(sta,y,0);
set(sta,y+1,0);
f[now][sta]+=val;
} else if(p1==2&&p2==2) { //(( '))'
set(sta,link(sta,y),2);
set(sta,y,0);
set(sta,y+1,0);
f[now][sta]+=val;
}
}
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1; i<=n; ++i) {
scanf("%s",a[i]+1);
for(int j=1; j<=m; ++j) {
if(a[i][j]=='.') endx=i,endy=j;
}
}
f[0].clear();
f[0][0]=1;
for(int i=1; i<=n; ++i) {
for(int j=1; j<=m; ++j) p_dp(i,j);
if(i!=n) {
int now=(i*m)&1,tot=f[now].siz;
for(int j=1; j<=tot; ++j)
f[now].key[j]<<=2;
}
}
printf("%lld\n",ans);
return 0;
}
luogu2289 邮递员
容易发现从\((1,1)\)出发再回到\((1,1)\)且所有点都恰号经过一次的方案数正是途中的曼哈顿回路数目*2(正着走和逆着走),高精度。
#include <bits/stdc++.h>
//using namespace std;
struct cint {
static const int P=1e9;
int bit[10];
cint() { clear();}
void clear() {
memset(bit,0,sizeof bit);
}
void set(int t) {
for(clear(); t; bit[++bit[0]]=t%P,t/=P);
}
int&operator[](const int &d) {
return bit[d];
}
void print(char ed='\n') {
printf("%d",bit[bit[0]]);
for(int i=bit[0]-1; i>0; --i) printf("%09d",bit[i]);
putchar(ed);
}
cint operator+(cint b) {
cint c;
c.clear();
c[0]=std::max(bit[0],b[0])+1;
for(int i=1; i<=c[0]; ++i) {
c[i]+=bit[i]+b[i];
c[i+1]+=c[i]/P;
c[i]%=P;
}
while(!c[c[0]]) c[0]--;
return c;
}
void operator+=(cint b) {
*this=*this+b;
}
void operator=(int x) {
set(x);
}
} ans;
struct hash_map {
static const int P=299987;
cint val[P];
int siz,key[P],hsh[P];
void clear() {
siz=0;
memset(val,0,sizeof val);
memset(key,-1,sizeof key);
memset(hsh,0,sizeof hsh);
}
void new_hsh(int id,int vl) {
hsh[id]=++siz,key[siz]=vl;
}
cint &operator[](const int &s) {
for(int i=s%P; ; i=(i+1==P?0:i+1)) {
if(!hsh[i]) new_hsh(i,s);
if(key[hsh[i]]==s) return val[hsh[i]];
}
}
} f[2];
int n,m;
int find(int sta,int id) {
return (sta>>((id-1)<<1))&3;
}
void set(int&sta,int bit,int val) {
bit=(bit-1)<<1;
sta|=3<<bit;
sta^=3<<bit;
sta|=val<<bit;
}
int link(int sta,int pos) {
int cnt=0,dlt=(find(sta,pos)==1?1:-1);
for(int i=pos; i&&i<=m+1; i+=dlt) {
int plg=find(sta,i);
if(plg==1) cnt++;
else if(plg==2) cnt--;
if(!cnt) return i;
}
return -1;
}
void p_dp(int x,int y) {
int now=((x-1)*m+y)&1,lst=now^1;
f[now].clear();
for(int i=1; i<=f[lst].siz; ++i) {
int sta=f[lst].key[i];
cint val=f[lst].val[i];
if(link(sta,y)==-1||link(sta,y+1)==-1) continue;
int p1=find(sta,y),p2=find(sta,y+1);
if(!p1&&!p2) {
if(x!=n&&y!=m) {
set(sta,y,1);
set(sta,y+1,2);
f[now][sta]+=val;
}
} else if(p1&&!p2) {
if(x!=n) f[now][sta]+=val;
if(y!=m) {
set(sta,y,0);
set(sta,y+1,p1);
f[now][sta]+=val;
}
} else if(!p1&&p2) {
if(y!=m) f[now][sta]+=val;
if(x!=n) {
set(sta,y,p2);
set(sta,y+1,0);
f[now][sta]+=val;
}
} else if(p1==1&&p2==1) {
set(sta,link(sta,y+1),1);
set(sta,y,0);
set(sta,y+1,0);
f[now][sta]+=val;
} else if(p1==1&&p2==2) {
if(x==n&&y==m) ans+=val;
} else if(p1==2&&p2==1) {
set(sta,y,0);
set(sta,y+1,0);
f[now][sta]+=val;
} else {
set(sta,link(sta,y),2);
set(sta,y,0);
set(sta,y+1,0);
f[now][sta]+=val;
}
}
}
int main() {
scanf("%d%d",&n,&m);
if(n==1||m==1) {
puts("1");
return 0;
}
if(n<m) std::swap(n,m);
f[0].clear();
f[0][0]=1;
for(int i=1; i<=n; ++i) {
for(int j=1; j<=m; ++j) p_dp(i,j);
if(i!=n) {
int now=(i*m)&1;
for(int j=1; j<=f[now].siz; ++j)
f[now].key[j]<<=2;
}
}
ans+=ans;
ans.print();
return 0;
}
bzoj2310 ParkII
求最大权任意路径,引入了独立插头
#include <bits/stdc++.h>
#define upd(sta,x) f[now][sta]=max(f[now][sta],(x))
using std::max;
struct hash_map {
static const int P=23333;
int siz,hsh[P],val[P],key[P];
void clear() {
siz=0;
memset(hsh,0,sizeof hsh);
memset(key,-1,sizeof key);
memset(val,-0x3f,sizeof val);
}
void new_hsh(int id,int sta) {
hsh[id]=++siz,key[siz]=sta;
}
int &operator[](int sta) {
for(int i=sta%P; ; i=(i+1==P?0:i+1)) {
if(!hsh[i]) new_hsh(i,sta);
if(key[hsh[i]]==sta) return val[hsh[i]];
}
}
} f[2];
int n,m,ans=-0x3f3f3f3f,a[101][101];
int find(int sta,int id) {
return (sta>>((id-1)<<1))&3;
}
void set(int &sta,int bit,int val) {
bit=(bit-1)<<1;
sta|=3<<bit;
sta^=3<<bit;
sta|=val<<bit;
}
int link(int sta,int pos) {
int cnt=0,dlt=(find(sta,pos)==1?1:-1);
for(int i=pos; i&&i<=m+1; i+=dlt) {
int plg=find(sta,i);
if(plg==1) cnt++;
else if(plg==2) cnt--;
if(cnt==0) return i;
}
return -1;
}
bool check(int sta) {
int cnt=0,cnt1=0;
for(int i=1; i<=m+1; ++i) {
int plg=find(sta,i);
if(plg==3) cnt++;
else if(plg==1) cnt1++;
else if(plg==2) cnt1--;
if(cnt>2/*||cnt1<0*/) break;
}
return cnt<=2&&cnt1==0;
}
void p_dp(int x,int y) {
int now=((x-1)*m+y)&1,lst=now^1;
f[now].clear();
for(int i=1; i<=f[lst].siz; ++i) {
int sta=f[lst].key[i];
int val=f[lst].val[i];
if(!check(sta)||sta>=(1<<((m+1)<<1))) continue;
int p1=find(sta,y);
int p2=find(sta,y+1);
int idl=sta;
set(idl,y,0);
set(idl,y+1,0);
int ept1=idl,ept2=idl;
if(!p1&&!p2) {
upd(idl,val); //跳过这个格子
if(x<n&&y<m) set(sta,y,1),set(sta,y+1,2),upd(sta,val+a[x][y]); //新建一对括号
if(x<n) set(ept1,y,3),upd(ept1,val+a[x][y]); //新建向下的独立插头
if(y<m) set(ept2,y+1,3),upd(ept2,val+a[x][y]); //新建向右的独立插头
} else if(p1&&!p2) {
if(x<n) upd(sta,val+a[x][y]); //向下扩展p1
if(y<m) set(ept1,y+1,p1),upd(ept1,val+a[x][y]); //向右扩展p1
if(p1==3) {if(!idl) ans=max(ans,val+a[x][y]);}
else set(ept2,link(sta,y),3),upd(ept2,val+a[x][y]); //停止扩展p1,p1的另一头改为独立插头
} else if(!p1&&p2) {
if(y<m) upd(sta,val+a[x][y]); //向右扩展p2
if(x<n) set(ept2,y,p2),upd(ept2,val+a[x][y]); //向下扩展p2
if(p2==3) {if(!idl) ans=max(ans,val+a[x][y]);}
else set(ept1,link(sta,y+1),3),upd(ept1,val+a[x][y]); //停止扩展p2,p2的另一头改为独立插头
}
else if(p1==1&&p2==1) set(ept1,link(sta,y+1),1),upd(ept1,val+a[x][y]); //'(('))
else if(p1==1&&p2==2) continue; //形成回路,不合法
else if(p1==2&&p2==1) upd(idl,val+a[x][y]); //(')(') 连接
else if(p1==2&&p2==2) set(ept2,link(sta,y),2),upd(ept2,val+a[x][y]); //(('))'
else if(p1==3&&p2==3) {if(!idl) ans=max(ans,val+a[x][y]);}
else if(p2==3) set(ept1,link(sta,y),3),upd(ept1,val+a[x][y]); //连接
else if(p1==3) set(ept2,link(sta,y+1),3),upd(ept2,val+a[x][y]); //连接
}
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1; i<=n; ++i) {
for(int j=1; j<=m; ++j) {
scanf("%d",&a[i][j]);
ans=max(ans,a[i][j]);
}
}
f[0].clear();
f[0][0]=0;
for(int i=1; i<=n; ++i) {
for(int j=1; j<=m; ++j) p_dp(i,j);
if(i!=n) {
int now=(i*m)&1;
for(int j=1; j<=f[now].siz; ++j)
f[now].key[j]<<=2;
}
}
printf("%d\n",ans);
return 0;
}
bzoj2331 [SCOI2011]地板
轮廓线上的状态0表示无插头,1表示有一个没有拐弯的插头,2表示拐过弯的插头。
#include <bits/stdc++.h>
const int mod=20110520;
struct hash_map {
static const int P=233333;
int siz,hsh[P],val[P],key[P];
void clear() {
siz=0;
memset(hsh,0,sizeof hsh);
memset(key,-1,sizeof key);
memset(val,0,sizeof val);
}
void new_hsh(int id,int sta) {
hsh[id]=++siz,key[siz]=sta;
}
int &operator[](int sta) {
for(int i=sta%P; ; i=(i+1==P?0:i+1)) {
if(!hsh[i]) new_hsh(i,sta);
if(key[hsh[i]]==sta) return val[hsh[i]];
}
}
} f[2];
int n,m,edx,edy,ans;
char a[102][102];
int find(int sta,int id) {
return (sta>>((id-1)<<1))&3;
}
void set(int &sta,int bit,int val) {
bit=(bit-1)<<1;
sta|=3<<bit;
sta^=3<<bit;
sta|=val<<bit;
}
#define upd(val) f[now][sta]=(f[now][sta]+(val))%mod;
void p_dp(int x,int y) {
int now=((x-1)*m+y)&1,lst=now^1;
f[now].clear();
for(int i=1; i<=f[lst].siz; ++i) {
int sta=f[lst].key[i];
int val=f[lst].val[i];
int p1=find(sta,y);
int p2=find(sta,y+1);
if(sta>=(1<<((m+1)<<1))) continue;
if(a[x][y]!='_') {
if(!p1&&!p2) upd(val);
} else if(!p1&&!p2) {
if(a[x+1][y]=='_') set(sta,y,1),set(sta,y+1,0),upd(val);
if(a[x][y+1]=='_') set(sta,y,0),set(sta,y+1,1),upd(val);
if(a[x][y+1]=='_'&&a[x+1][y]=='_') set(sta,y,2),set(sta,y+1,2),upd(val);
} else if(!p1&&p2) {
if(p2==1) {
if(a[x+1][y]=='_') set(sta,y,p2),set(sta,y+1,0),upd(val);
if(a[x][y+1]=='_') set(sta,y,0),set(sta,y+1,2),upd(val);
} else {
set(sta,y,0),set(sta,y+1,0),upd(val);
if(x==edx&&y==edy&&!sta) ans=(ans+val)%mod;
if(a[x+1][y]=='_') set(sta,y,2),upd(val);
}
} else if(p1&&!p2) {
if(p1==1) {
if(a[x][y+1]=='_') set(sta,y,0),set(sta,y+1,1),upd(val);
if(a[x+1][y]=='_') set(sta,y,2),set(sta,y+1,0),upd(val);
} else {
set(sta,y,0),set(sta,y+1,0),upd(val);
if(x==edx&&y==edy&&!sta) ans=(ans+val)%mod;
if(a[x][y+1]=='_') set(sta,y+1,2),upd(val);
}
} else if(p1==1&&p2==1) {
set(sta,y,0),set(sta,y+1,0),upd(val);
if(x==edx&&y==edy&&!sta) ans=(ans+val)%mod;
} //其余情况不合法
}
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1; i<=n; ++i) {
scanf("%s",a[i]+1);
}
if(n<m) { //转置
for(int i=1; i<=n; ++i) {
for(int j=i+1; j<=m; ++j)
std::swap(a[i][j],a[j][i]);
}
std::swap(n,m);
}
for(int i=1; i<=n; ++i) {
for(int j=1; j<=m; ++j) {
if(a[i][j]!='*') a[i][j]='_';
if(a[i][j]=='_') edx=i,edy=j;
}
}
f[0].clear();
f[0][0]=1;
for(int i=1; i<=n; ++i) {
for(int j=1; j<=m; ++j) p_dp(i,j);
if(i!=n) {
int now=(i*m)&1;
for(int j=1; j<=f[now].siz; ++j)
f[now].key[j]<<=2;
}
}
printf("%d",ans);
return 0;
}
luogu3886 [JLOI2009]神秘的生物
很裸的最小表示法的题目。
#include <bits/stdc++.h>
//using namespace std;
const int inf=0x3f3f3f3f;
struct hash_map {
static const int P=23333;
int siz,hsh[P],key[P],val[P];
void clear() {
siz=0;
memset(hsh,0,sizeof hsh);
memset(key,-1,sizeof key);
memset(val,-inf,sizeof val);
}
void new_hsh(int id,int sta) {
hsh[id]=++siz,key[siz]=sta;
}
int&operator[](int sta) {
for(int i=sta%P; ; i=(i+1==P?0:i+1)) {
if(!hsh[i]) new_hsh(i,sta);
if(key[hsh[i]]==sta) return val[hsh[i]];
}
}
} f[2];
int n,ans=-inf;
int now,lst=1,a[10][10];
int find(int sta,int bit) {
if(!bit) return 0;
return (sta>>(3*(bit-1)))&7;
}
void set(int&sta,int bit,int val) {
bit=3*(bit-1);
sta|=7<<bit;
sta^=7<<bit;
sta|=val<<bit;
}
int count(int sta,int val) {
int c=0;
for(int i=1; i<=n; ++i,sta>>=3)
if((sta&7)==val) c++;
return c;
}
int relabel(int sta) {
static int hsh,cnt,id[10],w[10];
memset(id,-1,sizeof id);
hsh=cnt=id[0]=0;
for(int i=1; i<=n; ++i,sta>>=3) w[i]=sta&7;
for(int i=n; i; --i) {
if(id[w[i]]==-1) id[w[i]]=++cnt;
hsh=hsh<<3|id[w[i]];
}
return hsh;
}
bool unicom(int sta) {
bool exi=0;
for(int i=1; i<=n; ++i,sta>>=3) {
if((sta&7)>1) return 0;
if((sta&7)==1) exi=1;
}
return exi;
}
#define upd(sta,val) f[now][sta]=std::max(f[now][sta],(val))
void p_dp(int x,int y) {
now=lst,lst^=1;
f[now].clear();
for(int i=1; i<=f[lst].siz; ++i) {
int sta=f[lst].key[i];
int val=f[lst].val[i];
int p1=find(sta,y-1);
int p2=find(sta,y);
if(!p1&&!p2) {
upd(sta,val);
set(sta,y,7);
upd(relabel(sta),val+a[x][y]);
} else if(!p1&&p2) {
if(count(sta,p2)==1) upd(sta,val+a[x][y]);
else {
upd(sta,val+a[x][y]);
set(sta,y,0);
upd(relabel(sta),val);
}
} else if(p1&&!p2) {
upd(sta,val);
set(sta,y,p1);
upd(relabel(sta),val+a[x][y]);
} else if(p1==p2) {
upd(sta,val+a[x][y]);
set(sta,y,0);
upd(relabel(sta),val);
} else {
if(count(sta,p2)==1) {
for(int j=1,tmp=sta; j<=n; ++j,tmp>>=3)
if((tmp&7)==p1) set(sta,j,p2);
upd(relabel(sta),val+a[x][y]);
} else {
int tmp=sta;
set(tmp,y,0);
upd(relabel(tmp),val);
tmp=sta;
for(int j=1; j<=n; ++j,tmp>>=3)
if((tmp&7)==p1) set(sta,j,p2);
upd(relabel(sta),val+a[x][y]);
}
}
}
for(int i=1; i<=f[now].siz; ++i) {
if(unicom(f[now].key[i])) ans=std::max(ans,f[now].val[i]);
}
}
int main() {
scanf("%d",&n);
f[now].clear();
f[now][0]=0;
for(int i=1; i<=n; ++i) {
for(int j=1; j<=n; ++j) {
scanf("%d",&a[i][j]);
p_dp(i,j);
}
}
printf("%d\n",ans);
}
bzoj2595 [WC2008]游览计划
斯坦纳树的解法详见最小斯坦纳树初探
插头dp(最小表示法)详见QAQ
bzoj1494 [NOI2007]生成树计数
假设已经考虑了前i-1个点,此时轮廓线定义为i-k到i-1的连通性(状态设为\(f[i-1,s]\))。k很小,搜索可知连续k位的连通性表示(最小表示法)不会超过55个。而且在i>=k时的状态的转移显然可以矩乘优化,只需要处理考虑i=k时前k位的连通性\(s\)的方案数。
可以参考论文
#include <bits/stdc++.h>
using namespace std;
const int N=55;
const int mod=65521;
int k,cnt,expr[N][6];
long long n;
struct mtr {
int a[N][N];
int*operator[](int x) {return a[x];}
mtr operator*(mtr b) {
static mtr c;
memset(&c,0,sizeof c);
for(int i=1; i<=cnt; ++i) {
for(int k=1; k<=cnt; ++k) {
for(int j=1; j<=cnt; ++j) {
c[i][j]=(c[i][j]+1u*a[i][k]*b[k][j])%mod;
}
}
}
return c;
}
mtr pow(long long y) {
static mtr x,c;
x=*this;
memset(&c,0,sizeof c);
for(int i=1; i<=cnt; ++i) c[i][i]=1;
for(; y; y>>=1,x=x*x) if(y&1) c=c*x;
return c;
}
} ans,A,B;
int id[8000],t[7],tmp[7],vis[7],cpl[6];
int qpow(int x,int y) {
int c=1;
for(; y>0; y>>=1,x=1u*x*x%mod)
if(y&1) c=1u*c*x%mod;
return c;
}
void dfs(int dep,int mx) {
if(dep==k+1) {
cnt++;
int hs=0;
memset(vis,0,sizeof vis);
for(int i=1; i<=k; ++i) {
expr[cnt][i]=t[i];
hs=hs*6+t[i];
vis[t[i]]++;
}
id[hs]=cnt;
B[cnt][1]=1;
for(int i=1; vis[i]; ++i) {
B[cnt][1]=1u*B[cnt][1]*cpl[vis[i]]%mod;
}
return;
}
for(int i=1; i<=mx; ++i) {
t[dep]=i,dfs(dep+1,mx);
}
t[dep]=mx+1;
dfs(dep+1,mx+1);
}
void init() {
for(int i=1; i<=k; ++i) cpl[i]=qpow(i,i-2);
dfs(1,0);
for(int i=1; i<=cnt; ++i) {
copy(expr[i]+1,expr[i]+k+1,t), t[k]=6;
copy(t,t+k+1,tmp);
for(int j=0; j<(1<<k); ++j) {
bool ok=1;
for(int p=0; p<k; ++p) if((j>>p)&1) {
int c=t[p];
if(c==6) { ok=0; break;}
for(int q=0; q<k; ++q) if(t[q]==c) t[q]=6;
}
if(ok) {
int tot=0,hs=0;
memset(vis,0,sizeof vis);
for(int p=1; p<=k; ++p) {
if(!vis[t[p]]) vis[t[p]]=++tot;
hs=hs*6+(t[p]=vis[t[p]]);
}
if(vis[t[0]]) (A[id[hs]][i]+=1)%=mod;
}
copy(tmp,tmp+k+1,t);
}
}
}
int main() {
scanf("%d%lld",&k,&n);
if(n<=k) {
printf("%d\n",qpow(n,n-2));
return 0;
}
init();
ans=A.pow(n-k)*B;
printf("%d\n",ans[1][1]);
return 0;
}
插头dp初探的更多相关文章
- 初探 插头DP
因为这题,气得我火冒三丈! 这数据是不是有问题啊!我用cin代替scanf后居然就AC了(本来一直卡在Test 18)!导致我调(对)试(排)了一个小时!! UPD:后来细细想想,会不会是因为scan ...
- 初探插头dp
开学那个月学了点新东西,不知道还记不记得了,mark一下 感觉cdq的论文讲的很详细 题主要跟着kuangbin巨做了几道基础的 http://www.cnblogs.com/kuangbin/arc ...
- [专题总结]初探插头dp
彻彻底底写到自闭的一个专题. 就是大型分类讨论,压行+宏定义很有优势. 常用滚动数组+哈希表+位运算.当然还有轮廓线. Formula 1: 经过所有格子的哈密顿回路数. 每个非障碍点必须有且仅有2个 ...
- 插头dp
插头dp 感受: 我觉得重点是理解,算法并不是直接想出怎样由一种方案变成另一种方案.而是方案本来就在那里,我们只是枚举状态统计了答案. 看看cdq的讲义什么的,一开始可能觉得状态很多,但其实灰常简单 ...
- HDU 4113 Construct the Great Wall(插头dp)
好久没做插头dp的样子,一开始以为这题是插头,状压,插头,状压,插头,状压,插头,状压,无限对又错. 昨天看到的这题. 百度之后发现没有人发题解,hust也没,hdu也没discuss...在acm- ...
- HDU 4949 Light(插头dp、位运算)
比赛的时候没看题,赛后看题觉得比赛看到应该可以敲的,敲了之后发现还真就会卡题.. 因为写完之后,无限TLE... 直到后来用位运算代替了我插头dp常用的decode.encode.shift三个函数以 ...
- 插头DP专题
建议入门的人先看cd琦的<基于连通性状态压缩的动态规划问题>.事半功倍. 插头DP其实是比较久以前听说的一个东西,当初是水了几道水题,最近打算温习一下,顺便看下能否入门之类. 插头DP建议 ...
- HDU 1693 Eat the Trees(插头DP、棋盘哈密顿回路数)+ URAL 1519 Formula 1(插头DP、棋盘哈密顿单回路数)
插头DP基础题的样子...输入N,M<=11,以及N*M的01矩阵,0(1)表示有(无)障碍物.输出哈密顿回路(可以多回路)方案数... 看了个ppt,画了下图...感觉还是挺有效的... 参考 ...
- HDU 1693 Eat the Trees(插头DP)
题目链接 USACO 第6章,第一题是一个插头DP,无奈啊.从头看起,看了好久的陈丹琦的论文,表示木看懂... 大体知道思路之后,还是无法实现代码.. 此题是插头DP最最简单的一个,在一个n*m的棋盘 ...
随机推荐
- JavaSE 集合类TreeSet存储自定义对象
文章目录 一.自动排序功能测试 二.对自定义类的自动排序 一.自动排序功能测试 public class TreeSetDemo { public static void main(String ar ...
- 三、PyQt5不同方法创建菜单栏、工具栏和状态栏
创建菜单栏.工具栏和状态栏可以直接通过代码实现,也可以通过Qt Designer中的属性编辑器等实现.通过两种方法的学习可以加深理解,更好的掌握PyQt5. 一.菜单栏与状态栏 状态栏的设置比较简单, ...
- 创建JavaScript函数的几种方式
window.onload = function() { // console.log('ok'); //正规的创建函数 function test(abc, d) { return abc(d); ...
- [js]jQuery EasyUI的linkbutton组件disable方法无法禁用jQuery绑定事件的问题分析
问题由来 linkbutton 是 jQuery EasyUI 中常用的一个控件,可以使用它创建按钮.用法很简单,使用 a 标签给一个easyui-linkbutton 的class就可以了. < ...
- Sorl 4.10 入门合集
Sorl4.10 + Tomcat 7.0 win7环境下的安装 1.首先是到apache官网下载sorl 4.10 ,解压 2.进入路径\solr-4.10.4\example\webapps,拷 ...
- nacos 使用记
本文记录SpringBoot和SpringCloud与Nacos作为配置中心的整合过程及问题 Nacos官方使用文档:https://nacos.io/zh-cn/docs/what-is-nacos ...
- JS中的同步和异步
javascript语言是一门“单线程”的语言,不像java语言,类继承Thread再来个thread.start就可以开辟一个线程,所以,javascript就像一条流水线,仅仅是一条流水线而已,要 ...
- C/C++字符串相关知识使用整理
C++字符串处理有最原始的char以及string两种方式,这里对其常用的功能进行总结. #include <string>using namespace std; ]; string s ...
- Redis各种数据类型的应用场景
redis是一种key values形式的非关系型数据库,通过内存存储,也可以把数据持久化到本地文件中. redis支持丰富的数据类型,String,list,set,zset,hash,下面说一下各 ...
- 自我理解node.js相比java的优势
今天学习node.js,相比于之前学习过的java,node.js有一些优越之处.原因是它是一个基于Chrome v8引擎建立的JavaScript运行平台. (1)创建服务器:自行服务器来监听客户端 ...