Luogu P3313 [SDOI2014]旅行
血的教训q-q
因为这个我WA了MLE了不知道多少次q-q
我终于自我察觉了,然后删了
然后就AC了
我和LHY说了我非常沙雕的错误
LHY非常好人教了我,如果需要赋值时应该这样
本题解法:树链剖分+动态开点线段树
对于这么多个宗教,每个宗教开一棵线段树
树链剖分寻找路径
再去线段树上查找,也就不用考虑途中城市是不是同一宗教
*数据保证询问的st和ed同一宗教
代码:
#include<cstdio>
#include<cstring>
int n,m,len=0;
struct nod1{int x,y,next;}b[200010];
struct nod2{int lc,rc,mx,tot;}tr[20000010];
int size[100010],son[100010],newid[100010],first[100010],f[100010];
int deep[100010],top[100010],w[100010],c[100010],root[100010];
int maxx(int x,int y)
{
return x>y?x:y;
}
int ins(int x,int y)
{
len++;
b[len].x=x;
b[len].y=y;
b[len].next=first[x];
first[x]=len;
}
int dfs1(int x)
{
size[x]=1;
for(int i=first[x];i;i=b[i].next)
{
int y=b[i].y;
if(y!=f[x])
{
deep[y]=deep[x]+1;
f[y]=x;
dfs1(y);
if(size[son[x]]<size[y])son[x]=y;
size[x]+=size[y];
}
}
}
int dfs2(int x,int ttop)
{
top[x]=ttop;
len++;newid[x]=len;
///id[len]=x;
if(son[x]!=0)dfs2(son[x],ttop);
for(int i=first[x];i;i=b[i].next)
{
int y=b[i].y;
if(y!=son[x]&&y!=f[x])
{
dfs2(y,y);
}
}
}
void update(int &now,int l,int r,int x,int k)
{
//printf("--%d %d %d %d %d--\n",now,l,r,x,k);
if(now==0)
{
len++;now=len;
}
tr[now].tot+=k;
if(l==r)
{
if(k>0)tr[now].mx=k;
else tr[now].mx=0;
return ;
}
int mid=(l+r)/2;
if(x<=mid)update(tr[now].lc,l,mid,x,k);
else update(tr[now].rc,mid+1,r,x,k);
tr[now].mx=maxx(tr[tr[now].lc].mx,tr[tr[now].rc].mx);
//printf("|%d %d|\n",tr[now].tot,tr[now].mx);
}
int change1(int x,int y)
{
update(root[c[x]],1,n,newid[x],-w[x]);
c[x]=y;
update(root[c[x]],1,n,newid[x],w[x]);
}
int change2(int x,int y)
{
update(root[c[x]],1,n,newid[x],-w[x]);
w[x]=y;
update(root[c[x]],1,n,newid[x],w[x]);
}
int swap(int &x,int &y)
{
int t=x;x=y;y=t;
}
int findsum(int now,int l,int r,int x,int y)
{
if(l==x&&y==r)return tr[now].tot;
int mid=(l+r)/2,lc=tr[now].lc,rc=tr[now].rc;
if(y<=mid)return findsum(lc,l,mid,x,y);
else if(mid+1<=x)return findsum(rc,mid+1,r,x,y);
else return findsum(lc,l,mid,x,mid)+findsum(rc,mid+1,r,mid+1,y);
}
int sumsum(int x,int y)
{
int sum=0;
int st=x;
int topx=top[x],topy=top[y];
while(topx!=topy)
{
if(deep[topx]>deep[topy])
{
swap(x,y);swap(topx,topy);
}
sum+=findsum(root[c[st]],1,n,newid[topy],newid[y]);
y=f[topy];topy=top[y];
}
if(deep[x]>deep[y])swap(x,y);
sum+=findsum(root[c[st]],1,n,newid[x],newid[y]);
printf("%d\n",sum);
}
int findmax(int now,int l,int r,int x,int y)
{
if(l==x&&y==r)return tr[now].mx;
int mid=(l+r)/2,lc=tr[now].lc,rc=tr[now].rc;
if(y<=mid)return findmax(lc,l,mid,x,y);
if(mid+1<=x)return findmax(rc,mid+1,r,x,y);
else return maxx(findmax(lc,l,mid,x,mid),findmax(rc,mid+1,r,mid+1,y));
}
int mxmx(int x,int y)
{
int ma=0;
int st=x;
int topx=top[x],topy=top[y];
while(topx!=topy)
{
if(deep[topx]>deep[topy])
{
swap(x,y);swap(topx,topy);
}
int g=findmax(root[c[st]],1,n,newid[topy],newid[y]);
if(g>ma)ma=g;
y=f[topy];topy=top[y];
}
if(deep[x]>deep[y])swap(x,y);
int g=findmax(root[c[st]],1,n,newid[x],newid[y]);
if(g>ma)ma=g;
printf("%d\n",ma);
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d %d",&w[i],&c[i]);
for(int i=1;i<=n-1;i++)
{
int x,y;
scanf("%d %d",&x,&y);
ins(x,y);ins(y,x);
}
len=0;
deep[1]=1;son[1]=0;
size[1]=0;f[1]=0;
dfs1(1);len=0;
dfs2(1,1);len=0;
/*for(int i=1;i<=n;i++)
{
printf("|%d |\n",root[c[i]]);
}*/
for(int i=1;i<=n;i++)
update(root[c[i]],1,n,newid[i],w[i]);
//ok
for(int i=1;i<=m;i++)
{
int x,y;
char ss[10];
scanf("%s",ss+1);
scanf("%d %d",&x,&y);
if(ss[2]=='C')change1(x,y);
else if(ss[2]=='W')change2(x,y);
else if(ss[2]=='S')sumsum(x,y);
else if(ss[2]=='M')mxmx(x,y);
}
}
代码+注释:
#include<cstdio>
#include<cstring>
int n,m,len=0;
struct nod1{int x,y,next;}b[200010];
struct nod2{int lc,rc,mx,tot;}tr[20000010];
int size[100010],son[100010],newid[100010],first[100010],f[100010];
int deep[100010],top[100010],w[100010],c[100010],root[100010];
int maxx(int x,int y)
{
return x>y?x:y;
}
int ins(int x,int y)
{
len++;
b[len].x=x;
b[len].y=y;
b[len].next=first[x];
first[x]=len;
}
int dfs1(int x)
{
size[x]=1;//以x为根的子树至少一个结点
for(int i=first[x];i;i=b[i].next)
{
int y=b[i].y;
if(y!=f[x])
{//不是父亲才能是自己儿子
deep[y]=deep[x]+1;
f[y]=x;
dfs1(y);
if(size[son[x]]<size[y])son[x]=y;
//如果你比我当前重儿子还厉害,那我让你当重儿子
size[x]+=size[y];
//你的后代也是我的后代
}
}
}
int dfs2(int x,int ttop)
//x和当前链的顶端
{
top[x]=ttop;//更新我的top
len++;newid[x]=len;//x在树链剖分维护的线段树上的新编号
if(son[x]!=0)dfs2(son[x],ttop);
//如果我有重儿子,他和我一条重链,他的top和我一样,先更新他
for(int i=first[x];i;i=b[i].next)
{
int y=b[i].y;
if(y!=son[x]&&y!=f[x])
{
dfs2(y,y);//轻儿子们自己是重链
}
}
}
void update(int &now,int l,int r,int x,int k)
{
if(now==0)
{
len++;now=len;//动态开点
}
tr[now].tot+=k;
if(l==r)
{
if(k>0)tr[now].mx=k;
else tr[now].mx=0;
return ;
}
int mid=(l+r)/2;
if(x<=mid)update(tr[now].lc,l,mid,x,k);
else update(tr[now].rc,mid+1,r,x,k);
tr[now].mx=maxx(tr[tr[now].lc].mx,tr[tr[now].rc].mx);
}
int change1(int x,int y)
{
update(root[c[x]],1,n,newid[x],-w[x]);
c[x]=y;
update(root[c[x]],1,n,newid[x],w[x]);
}
int change2(int x,int y)
{
update(root[c[x]],1,n,newid[x],-w[x]);
w[x]=y;
update(root[c[x]],1,n,newid[x],w[x]);
}
int swap(int &x,int &y)
{
int t=x;x=y;y=t;
}
int findsum(int now,int l,int r,int x,int y)
{//线段树查找
if(l==x&&y==r)return tr[now].tot;
int mid=(l+r)/2,lc=tr[now].lc,rc=tr[now].rc;
if(y<=mid)return findsum(lc,l,mid,x,y);
else if(mid+1<=x)return findsum(rc,mid+1,r,x,y);
else return findsum(lc,l,mid,x,mid)+findsum(rc,mid+1,r,mid+1,y);
}
int sumsum(int x,int y)
{
int sum=0;
int st=x;
int topx=top[x],topy=top[y];
while(topx!=topy)//还没跳到一条链上
{
if(deep[topx]>deep[topy])
{//我们跳y所以要保证y的深度是比x小的
swap(x,y);swap(topx,topy);
}
sum+=findsum(root[c[st]],1,n,newid[topy],newid[y]);
//累加跳掉(离开)的这条链的值
y=f[topy];topy=top[y];
//把y跳上另一条链
}
if(deep[x]>deep[y])swap(x,y);
sum+=findsum(root[c[st]],1,n,newid[x],newid[y]);
//因为他们在一条链上之后就退出来了,还没有累加这条链
printf("%d\n",sum);
}
int findmax(int now,int l,int r,int x,int y)
{//线段树查找
if(l==x&&y==r)return tr[now].mx;
int mid=(l+r)/2,lc=tr[now].lc,rc=tr[now].rc;
if(y<=mid)return findmax(lc,l,mid,x,y);
if(mid+1<=x)return findmax(rc,mid+1,r,x,y);
else return maxx(findmax(lc,l,mid,x,mid),findmax(rc,mid+1,r,mid+1,y));
}
int mxmx(int x,int y)
{//注释见sumsum 差不多la———
int ma=0;
int st=x;
int topx=top[x],topy=top[y];
while(topx!=topy)
{
if(deep[topx]>deep[topy])
{
swap(x,y);swap(topx,topy);
}
int g=findmax(root[c[st]],1,n,newid[topy],newid[y]);
if(g>ma)ma=g;
y=f[topy];topy=top[y];
}
if(deep[x]>deep[y])swap(x,y);
int g=findmax(root[c[st]],1,n,newid[x],newid[y]);
if(g>ma)ma=g;
printf("%d\n",ma);
}
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d %d",&w[i],&c[i]);
for(int i=1;i<=n-1;i++)
{
int x,y;
scanf("%d %d",&x,&y);
ins(x,y);ins(y,x);
}
len=0;
deep[1]=1;son[1]=0;
size[1]=0;f[1]=0;
dfs1(1);len=0;
dfs2(1,1);len=0;
for(int i=1;i<=n;i++)
update(root[c[i]],1,n,newid[i],w[i]);
//先把初始信息添加
for(int i=1;i<=m;i++)
{
int x,y;
char ss[10];
scanf("%s",ss+1);
scanf("%d %d",&x,&y);
if(ss[2]=='C')change1(x,y);
else if(ss[2]=='W')change2(x,y);
else if(ss[2]=='S')sumsum(x,y);
else if(ss[2]=='M')mxmx(x,y);
}
}
树链剖分好像也不是很坏(还是比较友善吧)qvq