Luogu P3313 [SDOI2014]旅行

3 篇文章 0 订阅
1 篇文章 0 订阅
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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值