51nod 1782 圣诞树

题意

ξ 得到了一棵圣诞树,他需要在上面挂满礼物。
ξ 会事先进行m个操作,每次在一条链(u[i],v[i])上的每个点上挂上a[i]个种类为b[i]的礼物。
一个点的k-美观度这样计算:把这个点上的所有种类的礼物按照个数从小到大排序,如果个数一样就按照种类从小到大排。
它的k-美观度就是排好序后前k种礼物种类的xor值(如果礼物种类不足k种,就把这个点上所有礼物的种类xor起来)。
接下来给Q个询问,给定w[i],k[i],求点w[i]的k[i]-美观度。

前言

这题真是做的我**了。。
想了一个早上,然后写了两个错误的想法。。
然后一个上午就成功过去了。。
什么都没做
下午又调了一个下午。。
一天就没了QAQ

题解

容易想到,这种东西可以树上差分
两个端点处挂上a[i]个b[i],在端点lca及lca的父亲处挂上-a[i]个b[i],询问子树的k-美观度
子树可以想到dfs序
因为dfs序的特殊性,所以所有区间都是包含或相离的
然后就可以分治了啊。。
考虑扩过中界线的
那么肯定是可以单调的啊
加入新的点,维护信息就用一个splay就可以了

要注意的是,在分治结构中,要注意所有全局变量的初始化,我就因为有一个没搞好调了很久QAQ

CODE:

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
typedef pair<int,int> PI;
const int N=100005;
int n,m;
struct qq
{
    int x,y,last;
}e[N*2];int num,last[N];
void init (int x,int y)
{
    num++;
    e[num].x=x;e[num].y=y;
    e[num].last=last[x];
    last[x]=num;
}
int fa[N][21];
int dep[N];
int L[N],R[N],cnt;
void dfs (int x)
{
    L[x]=++cnt;
    for (int u=1;u<=20;u++) fa[x][u]=fa[fa[x][u-1]][u-1];
    for (int u=last[x];u!=-1;u=e[u].last)
    {
        int y=e[u].y;
        if (y==fa[x][0]) continue;
        dep[y]=dep[x]+1;fa[y][0]=x;dfs(y);
    }
    R[x]=cnt;
}
int get (int x,int y)
{
    if (dep[x]>dep[y]) swap(x,y);
    for (int u=20;u>=0;u--)
        if (dep[fa[y][u]]>=dep[x])
            y=fa[y][u];
    if (x==y) return x;
//  printf("lalal:%d %d\n",x,y);
    for (int u=20;u>=0;u--)
        if (fa[x][u]!=fa[y][u])
            {x=fa[x][u];y=fa[y][u];}
    return fa[x][0];
}
vector<PI> s[N];//各种修改操作 
int Q;
int ans[N];
struct qt{int l,r,k,id;}q[N];
struct qr
{
    int f,son[2],c;//父亲   儿子   有多少个点   
    int a,b;//次数和值
    int ans;//子树的答案 
}tr[N*3];
bool operator < (const qr &x,const qr &y){return x.a==y.a?x.b<y.b:x.a<y.a;}
int root;
int vis[N];//这个种类是什么点 
queue<int> o;//我们用了哪些种类
void update (int x)
{
    int s1=tr[x].son[0],s2=tr[x].son[1];
    tr[x].c=tr[s1].c+tr[s2].c+1;
    tr[x].ans=(tr[s1].ans^tr[s2].ans^tr[x].b);
    return ;
}
void add (int a,int b,int fa)//新建一个节点 
{
    num++;vis[b]=num;
    tr[num].f=fa;
    tr[num].son[0]=tr[num].son[1]=0;
    tr[num].a=a;tr[num].b=b;
    tr[num].c=1;tr[num].ans=b;

    if (tr[num]<tr[fa]) tr[fa].son[0]=num;
    else tr[fa].son[1]=num;
}
int find (int a,int b)//反正肯定不存在,随便找一个就好了。。 
{
    int x=root;
    qr lalal;lalal.a=a;lalal.b=b;
    while (true)
    {
        if (lalal<tr[x])
        {
            if (tr[x].son[0]==0) break;
            x=tr[x].son[0];
        }
        else
        {
            if (tr[x].son[1]==0) break;
            x=tr[x].son[1];
        }
    }
    return x;
}
void rotate (int x)
{
    int y=tr[x].f,z=tr[y].f,r,R;
    int w=(tr[y].son[0]==x);

    r=tr[x].son[w];R=y;
    tr[R].son[1-w]=r;
    if (r!=0) tr[r].f=R;

    r=x;R=z;
    if (tr[R].son[0]==y) tr[R].son[0]=x;
    else tr[R].son[1]=x;
    tr[r].f=R;

    r=y;R=x;
    tr[R].son[w]=r;
    if (r!=0) tr[r].f=R;

    update(y);update(x);
}
void splay (int x,int rt)
{
    update(x);
    while (tr[x].f!=rt)
    {
        int y=tr[x].f,z=tr[y].f;
        if (z==rt) rotate(x);
        else
        {
            if ((tr[y].son[0]==x)==(tr[z].son[0]==y)) rotate(y);
            else rotate(x);
            rotate(x);
        }
    }
    if (rt==0) root=x;
}
void ins (int a,int b)//加入一个点   出现次数是a,种族是b  保证不存在 
{
    if (root==0)    {add(a,b,0);root=num;return ;}
    int x=find(a,b);
    add(a,b,x);
//  printf("TYB:%d %d %d\n",a,b,x);
    splay(x,0);
}
void del (int x)//删除这个点 
{
    splay(x,0);
    if (tr[x].son[0]==0&&tr[x].son[1]==0)   {num=root=0;return ;}
    if (tr[x].son[0]==0&&tr[x].son[1]!=0)   {root=tr[x].son[1];tr[root].f=0;return ;}
    if (tr[x].son[0]!=0&&tr[x].son[1]==0)   {root=tr[x].son[0];tr[root].f=0;return ;}
    else
    {
        int p=tr[x].son[0];
        while (tr[p].son[1]!=0) p=tr[p].son[1];
        splay(p,x);
        root=p;tr[root].f=0;
        int r=tr[x].son[1],R=root;
        tr[R].son[1]=r;tr[r].f=R;
        update(R);
    }
}
void print (int now)
{
    if (now==0) return ;
    printf("%d:%d %d ans:%d s1:%d s2:%d\n",now,tr[now].a,tr[now].b,tr[now].ans,tr[now].son[0],tr[now].son[1]);
    print(tr[now].son[0]);
    print(tr[now].son[1]);
}
void Ins (int x)//把这个点的信息加入进来 
{
//  printf("YES:%d\n",x);
    int tot=s[x].size();
    for (int u=0;u<tot;u++)
    {
        int xx=s[x][u].second;//加入这个种类
        //printf("%d %d\n",s[x][u].first,xx);
        if (vis[xx]==0) {o.push(xx);ins(s[x][u].first,xx);}
        else
        {
            int now=vis[xx];
            del(now);
            if (tr[now].a+s[x][u].first==0)//如果是 
                vis[xx]=0;
            else ins(tr[now].a+s[x][u].first,tr[now].b);
        }
    //  printf("O:%d\n",tr[root].ans);
    //print(root);

    }/*system("pause");
    printf("NO\n");*/

}
int get (int k)//排名第k的节点是什么 
{
    int x=root;
    while (true)
    {
        int s1=tr[x].son[0],s2=tr[x].son[1];
        if (k<=tr[s1].c) x=s1;
        else if (k>tr[s1].c+1) x=s2,k=k-tr[s1].c-1;
        else break;
    }
    return x;
}
int find (int k)//寻找前k个 
{
    if (tr[root].c<=k)  return tr[root].ans;
    int x=get(k+1);splay(x,0);
    return tr[tr[x].son[0]].ans;
}
queue<qt> A,B,C;
bool cmpq(qt x,qt y){return x.l==y.l?x.r<y.r:x.l>y.l;}
void solve (int l,int r,int L,int R)//处理到的区间     要处理的询问 
{
    if (L>R) return ;// printf("TYB:%d %d %d %d\n",l,r,L,R);
    if (l==r)
    {
        Ins(l);
    //  printf("YES\n");
        for (int u=L;u<=R;u++)  ans[q[u].id]=find(q[u].k);
        while (!o.empty())  {int xx=o.front();vis[xx]=0;o.pop();}   
        root=num=0;
        return ;
    }
    int mid=(l+r)>>1;
//  printf("l:%d r:%d mid:%d %d %d\n",l,r,mid,L,R);
    for (int u=L;u<=R;u++)
    {
        if (q[u].r<mid) 
        {
        //  printf("A;%d %d\n",q[u].l,q[u].r);
            A.push(q[u]);
        }
        else if (q[u].l>mid) 
        {
        //  printf("B:%d %d\n",q[u].l,q[u].r);
            B.push(q[u]);
        }
        else C.push(q[u]);
    }
    //C这次要处理的,A递归到左边的,B递归到右边的 
    int g=L,h=L;
    int now=L; 
    while (!A.empty())  {q[now]=A.front();A.pop();now++;}h=now;
    while (!B.empty())  {q[now]=B.front();B.pop();now++;}g=now;
    while (!C.empty())  {q[now]=C.front();C.pop();now++;}
    solve(l,mid-1,L,h-1);solve(mid+1,r,h,g-1);
    //那么now到现在的就是我们要处理的了
    if (g>R) return ;
    sort(q+g,q+1+R,cmpq); 
    int ll=mid,rr=mid;
/*printf("OZY:l:%d r:%d g:%d R:%d\n",l,r,g,R);
    for (int u=g;u<=R;u++)
        printf("%d %d\n",q[u].l,q[u].r);
    system("pause");*/
    for (int u=g;u<=R;u++)
    {
        while(ll>=q[u].l) {Ins(ll);ll--;}
        while (rr<q[u].r) {rr++;Ins(rr);}
        ans[q[u].id]=find(q[u].k);
    }
    while (!o.empty())  {int xx=o.front();vis[xx]=0;o.pop();}   
    root=num=0;
}
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
inline void write(int x)
{
    if (!x) return (void)puts("0");
    if (x < 0) putchar('-'), x = -x;
    static short s[12], t;
    while (x) s[++t] = x % 10, x /= 10;
    while (t) putchar('0' + s[t--]);
    putchar('\n');
}
int main()
{
    memset(vis,0,sizeof(vis));
    num=0;memset(last,-1,sizeof(last));
    n=read();
    for (int u=1;u<n;u++)
    {
        int x,y;
        x=read();y=read();
        init(x,y);init(y,x);
    }
    num=0;
    dep[1]=1;dfs(1);
    m=read();
    for (int u=1;u<=m;u++)
    {
        int x,y,a,b;
        x=read();y=read();a=read();b=read();
        int lca=get(x,y);
        s[L[x]].push_back(make_pair(a,b));
        s[L[y]].push_back(make_pair(a,b));
    //  printf("LCA:%d\n",lca);
        s[L[lca]].push_back(make_pair(-a,b));
        s[L[fa[lca][0]]].push_back(make_pair(-a,b));
    }
    Q=read();
    for (int u=1;u<=Q;u++)
    {
        int w,k;
        w=read();k=read();
        q[u].id=u;q[u].l=L[w];q[u].r=R[w];q[u].k=k;
    }
/*  for (int u=1;u<=Q;u++)
        printf("lalal:%d %d %d\n",q[u].l,q[u].r,q[u].k);*/
    solve(1,n,1,Q);
    for (int u=1;u<=Q;u++) write(ans[u]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值