[HDU 4348]To the moon | 可持久化线段树学习笔记

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4348
给定一个长度为N的数组,要求对数组执行M次指定的区间修改、区间和查询、查询历史版本的区间和、回滚到历史版本。
因为输入数据并没有进行加密,所以完全可以读入所有的数据后进行离线操作(但这样就没意思了,手动滑稽),但如果数据需要前一个查询的结果进行解密,强制在线,那就只能用可持久化线段树,即主席树进行维护。
可持久化数据结构的核心思想就是最大限度地利用之前版本的信息,减少增加记录的空间消耗。对线段树而言,就是将所有未修改的节点指针指向上一个版本的节点。
这一题还有一个要注意的地方,如果更新节点时将节点的lazy_tag向下传递,新增节点,内存是肯定炸光的,因此需要对查询操作特殊处理。
以下是代码,因为使用了指针和struct,稍微改一改就是一个模板(当然也更加难调试):

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <stack>
using namespace std;
const int MaxN = 200003;
typedef vector<int> vec;
typedef vec::iterator iter;
int n,m,q;
vec e[MaxN],g[MaxN];
int b[MaxN],blocks;
bool vis[MaxN];
void tarjan(int x,int fa) {
static int low[MaxN],dfn[MaxN],idx;
static bool instk[MaxN];
static stack<int> stk;
low[x]=dfn[x]=++idx;
vis[x]=true,stk.push(x),instk[x]=true;
bool flag=true;
for (iter i=e[x].begin();i!=e[x].end();++i) {
if (flag && *i==fa) {
flag=false;
continue;
}
if (!vis[*i]) {
tarjan(*i,x);
low[x]=min(low[x],low[*i]);
} else if (instk[*i]) {
low[x]=min(low[x],dfn[*i]);
}
}
if (low[x]==dfn[x]) {
int t;
blocks++;
do {
t=stk.top();stk.pop();
b[t]=blocks;instk[t]=false;
} while (t!=x);
}
}
int fa[MaxN],sz[MaxN],dep[MaxN],head[MaxN],id[MaxN],num[MaxN];
void build(int x) {
sz[x]=1;
for (iter i=g[x].begin();i!=g[x].end();++i) {
if (*i==fa[x]) continue;
fa[*i]=x,dep[*i]=dep[x]+1,num[*i]=num[x];
build(*i);
sz[x]+=sz[*i];
}
}
void part(int x) {
static int idx;
id[x]=++idx;
if (head[x]==0) head[x]=x;
int p=0;
for (iter i=g[x].begin();i!=g[x].end();++i) {
if (*i==fa[x]) continue;
if (p==0 || sz[*i]>sz[p]) p=*i;
}
if (p==0) return;
head[p]=head[x];
part(p);
for (iter i=g[x].begin();i!=g[x].end();++i) {
if (*i==fa[x] || *i==p) continue;
part(*i);
}
}
char up[MaxN<<2],down[MaxN<<2];
void cover(char* s ,int p,int l,int r,int ll,int rr) {
if (ll<=l && r<=rr) {
s[p]=2;
return;
}
if (s[p]==2) return;
s[p]=1;
int mid=(l+r)/2;
if (ll<=mid) cover(s,p*2,l,mid,ll,rr);
if (rr>mid) cover(s,p*2+1,mid+1,r,ll,rr);
if (s[p*2]==2 && s[p*2+1]==2) s[p]=2;
}
inline void cover(char* s,int l,int r) { if (l>r) return;cover(s,1,1,blocks,l,r); }
inline bool query(char* s,int x) {
int p=1,l=1,r=blocks,mid;
while (l<r) {
if (s[p]==2) return true;
if (s[p]==0) return false;
mid=(l+r)/2;
if (x<=mid) p=p*2,r=mid;
else p=p*2+1,l=mid+1;
}
return (s[p]!=0);
}
inline void deal(int u,int v) {
while (u!=v) {
if (head[u]==head[v]) {
if (dep[u]<dep[v])
cover(down,id[u]+1,id[v]);
else
cover(up,id[v]+1,id[u]);
break;
}
if (dep[head[u]]<dep[head[v]]) {
int t=head[v];
if (fa[t]==t) cover(down,id[t]+1,id[v]);
else cover(down,id[t],id[v]);
v=fa[t];
} else {
int t=head[u];
if (fa[t]==t) cover(up,id[t]+1,id[u]);
else cover(up,id[t],id[u]);
u=fa[t];
}
}
}
inline void fail() {
puts("No");
exit(0);
}
int main() {
scanf("%d%d%d",&n,&m,&q);
for (int i=1;i<=m;++i) {
int u,v;
scanf("%d%d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
}
for (int i=1;i<=n;++i) if (!vis[i]) tarjan(i,-1);
for (int i=1;i<=n;++i)
for (iter j=e[i].begin();j!=e[i].end();++j)
if (b[i]!=b[*j]) {
g[b[i]].push_back(b[*j]);
g[b[*j]].push_back(b[i]);
}
for (int i=1;i<=blocks;++i) { sort(g[i].begin(),g[i].end());g[i].erase(unique(g[i].begin(),g[i].end()),g[i].end()); }
int cnt;
for (int i=1;i<=blocks;++i) if (!num[i]) {
fa[i]=i,dep[i]=0,num[i]=++cnt;
build(i);
part(i);
}
for (int i=1;i<=q;++i) {
int u,v;
scanf("%d%d",&u,&v);
if (b[u]==b[v]) continue;
if (num[b[u]]!=num[b[v]]) fail();
deal(b[u],b[v]);
}
for (int i=1;i<=blocks;++i) if (fa[i]!=i) {
if (query(up,i) && query(down,i)) fail();
}
puts("Yes");
return 0;
}

暂无评论

发表回复

您的电子邮箱地址不会被公开。