AGC026D Histogram Coloring
Histogram Coloring
给定一个\(10^9\)行\(n\)列的表格,从左往右、从上往下标号,并去掉一些格子,使得第\(i\)列只剩下下面的\(h_i\)行格子。
要求对剩下的格子染成红蓝两种颜色,使得每个\(2 × 2\)的格子里面有恰好\(2\)个红色格子、 \(2\)个蓝色格子。
求方案数,对大质数取模。
\(n ≤ 100, ∀1 ≤ i ≤ n, h_i ≤ 10^9\)
题解
仓鼠《动态规划选讲》。
首先考虑一个完整的矩形如何计数答案
假如已经决定了前面的行,要新增一行。首先可以把上面一行全部翻转当成新增一行;特殊地,当上面一行是黑白相间的,可以直接把上面一行复制下来。
对于原问题考虑笛卡尔树DP,实际上就是每次把最低的一层给消掉。这样也构成了一个树的结构,然后从下往上DP,每次记录是否黑白相间的情况。
转移需要把所有上面的层合并,讨论一些情况。
时间复杂度 \(O(n\log n)\)。
CO int N=110;
int n;
namespace seg{
pair<int,int> val[4*N];
#define lc (x<<1)
#define rc (x<<1|1)
#define mid ((l+r)>>1)
void build(int x,int l,int r){
if(l==r) {val[x]={read<int>(),l}; return;}
build(lc,l,mid),build(rc,mid+1,r);
val[x]=min(val[lc],val[rc]);
}
pair<int,int> query(int x,int l,int r,int ql,int qr){
if(ql<=l and r<=qr) return val[x];
if(qr<=mid) return query(lc,l,mid,ql,qr);
if(ql>mid) return query(rc,mid+1,r,ql,qr);
return min(query(lc,l,mid,ql,qr),query(rc,mid+1,r,ql,qr));
}
#undef lc
#undef rc
#undef mid
}
int num,L[N],R[N],H[N];
vector<int> to[N];
int build(int l,int r){
int x=++num;
L[x]=l,R[x]=r,H[x]=seg::query(1,1,n,l,r).first;
vector<int> pos;
for(int i=l;i<=r;){
pair<int,int> v=seg::query(1,1,n,i,r);
if(v.first>H[x]) break;
pos.push_back(v.second);
i=v.second+1;
}
pos.push_back(r+1);
int last=l-1;
for(int now:pos){
if(last+1<=now-1){
to[x].push_back(build(last+1,now-1));
H[to[x].back()]-=H[x];
}
last=now;
}
return x;
}
int F[N][2],coef=1;
void dfs(int x){
// cerr<<x<<" "<<L[x]<<" "<<R[x]<<" H="<<H[x]<<endl;
int len=R[x]-L[x]+1;
for(int y:to[x]) dfs(y),len-=R[y]-L[y]+1;
F[x][0]=fpow(2,len),F[x][1]=2;
for(int y:to[x]){
F[x][0]=mul(F[x][0],add(F[y][0],mul(2,F[y][1])));
F[x][1]=mul(F[x][1],F[y][1]);
}
F[x][0]=add(F[x][0],mod-F[x][1]);
F[x][1]=mul(F[x][1],fpow(2,H[x]-1));
// cerr<<x<<" F="<<F[x][0]<<" "<<F[x][1]<<endl;
}
int main(){
read(n);
seg::build(1,1,n);
int root=build(1,n);
dfs(root);
int ans=add(F[root][0],F[root][1]);
ans=mul(ans,coef);
printf("%d\n",ans);
return 0;
}
静渊以有谋,疏通而知事。