• codeforces 603E Pastoral Oddities 题解


    大家好,我是题解搬运工

    http://blog.csdn.net/u014609452/article/details/54020829

    https://post.icpc-camp.org/d/324-codeforces-round-334-e-pastoral-oddities

    介于题解较少,且对于我来说比较难理解,于是决定自己重新写一遍题解

    题目大意,选前i条边中的一些边,要求每个点的度都为奇数,且最长边最短。

    这道题真是非常难想,首先我们考虑静态的做法。先给出结论,按照边的长短从小到大加入,直到每个联通块的大小都为偶数为止。

    如果一个联通块的节点的个数为偶数,那么我们一定可以在联通块中删除一些边,使得其中每个点的度数都为奇数。(我们从叶子节点开始,逐一排查,不行就删边,反正这个结论挺难想的QAQ)

    知道这个结论我们就好办了,题目变为用尽量少的边使得每个联通块点数为奇数。下面根据第一个题解,有3种做法

    1.LCT

    LCT 维护子树异或和与最大值(经典题)

    每次操作,考虑第i跳边,x,y,如果xy不连通,则连通,否则看它们的路径上最大的边的权值和当前v的大小,若大于,则删边,加边

    在每次加边删边的时候,通过子树大小异或和动态维护此时状态,比较经典的LCT题吧

      1 //start:   finish:
      2 #include<bits/stdc++.h>
      3 #define maxn 400005
      4 using namespace std;
      5 int n,m;
      6 int odd;
      7 bool rev[maxn];
      8 int c[maxn][2],fa[maxn];
      9 struct node{
     10     int mx,val;
     11     int sub,all,zhi;
     12 }a[maxn];
     13 pair <int,int> bian[maxn];
     14 set <pair<int,int> > s;
     15 int st[maxn];
     16 bool isroot(int x){
     17     return (c[fa[x]][0]!=x && c[fa[x]][1]!=x);
     18 }
     19 int MAX(int x,int y){
     20     if(!x || !y)return x+y;
     21     if(a[x].val>a[y].val)return x;
     22     return y;
     23 }
     24 void update(int x){
     25     a[x].all=a[x].sub^a[x].zhi;
     26     if(c[x][0])a[x].all^=a[c[x][0]].all;
     27     if(c[x][1])a[x].all^=a[c[x][1]].all;
     28     a[x].mx=x;
     29     a[x].mx=MAX(a[x].mx,a[c[x][0]].mx);
     30     a[x].mx=MAX(a[x].mx,a[c[x][1]].mx);
     31 }
     32 void down(int x){
     33     if(rev[x]){
     34         rev[c[x][0]]^=1;
     35         rev[c[x][1]]^=1;
     36         rev[x]^=1;
     37         swap(c[x][0],c[x][1]);
     38     }
     39 }
     40 void rotate(int x){
     41     int y=fa[x],z=fa[y];
     42     int l,r;
     43     if(c[y][0]==x)l=0;else l=1;
     44     r=1-l;
     45     if(!isroot(y)){
     46         if(c[z][0]==y)c[z][0]=x;
     47         else c[z][1]=x;
     48     }
     49     fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
     50     c[y][l]=c[x][r];c[x][r]=y;
     51     update(y);update(x);
     52 }
     53 void splay(int x){
     54     int top=0;st[++top]=x;
     55     for(int i=x;!isroot(i);i=fa[i])st[++top]=fa[i];
     56     for(int i=top;i;i--)down(st[i]);
     57     while(!isroot(x)){
     58         int y=fa[x],z=fa[y];
     59         if(!isroot(y)){
     60             if(c[y][0]==x^c[z][0]==y)rotate(x);
     61             else rotate(y);
     62         }
     63         rotate(x);
     64     }
     65 }
     66 void access(int x){
     67     int t=0;
     68     while(x){
     69         splay(x);
     70         down(x);
     71         if(t)a[x].sub^=a[t].all;
     72         if(c[x][1])a[x].sub^=a[c[x][1]].all;
     73         c[x][1]=t;
     74         update(x);
     75         t=x;
     76         x=fa[x];
     77     }
     78 }
     79 void move_to_root(int x){
     80     access(x);splay(x);rev[x]^=1;
     81 }
     82 int find(int x){
     83     access(x);splay(x);
     84     while(c[x][0])x=c[x][0];
     85     return x;
     86 }
     87 void split(int x,int y)
     88 {
     89     move_to_root(y);
     90     access(x);
     91     splay(x);
     92 }
     93 void link(int x,int y){
     94     move_to_root(x);
     95     move_to_root(y);
     96     fa[x]=y;a[y].sub^=a[x].all;
     97     update(y);
     98 }
     99 void cut(int x,int y){
    100     move_to_root(x);
    101     access(y);splay(y);
    102     c[y][0]=0;fa[x]=0;
    103     update(y);
    104 }
    105 void Link(int o){
    106     int x=bian[o].first,y=bian[o].second;
    107     move_to_root(x);move_to_root(y);
    108     if(a[x].all && a[y].all)odd-=2;
    109     link(x,o+n);
    110     link(y,o+n);
    111 }
    112 void Cut(int o){
    113     int x=bian[o].first,y=bian[o].second;
    114     cut(x,o+n);cut(y,o+n);
    115 }
    116 bool shan(int o){
    117     int x=bian[o].first,y=bian[o].second;
    118     cut(x,o+n);cut(y,o+n);
    119     move_to_root(x);move_to_root(y);
    120     if(!a[x].all && !a[y].all)return true;
    121     link(x,o+n);link(y,o+n);
    122     return false;
    123 }
    124 int main(){
    125     scanf("%d%d",&n,&m);
    126     odd=n;
    127     for(int i=1;i<=n;i++){
    128         a[i].all=a[i].zhi=1;
    129     }
    130     for(int i=1;i<=m;i++){
    131         int x,y,z;
    132         scanf("%d%d%d",&x,&y,&z);
    133         a[i+n].val=z;a[i+n].mx=i+n;
    134         bian[i]=make_pair(x,y);
    135         if(find(x)!=find(y)){
    136             Link(i);
    137             s.insert(make_pair(-z,i));
    138         }
    139         else{
    140             split(x,y);int e=a[x].mx;
    141             if(a[e].val>z){
    142                 Cut(e-n);
    143                 link(i+n,x);
    144                 link(i+n,y);
    145                 s.erase(make_pair(-a[e].val,e-n));
    146                 s.insert(make_pair(-z,i));
    147             }
    148         }
    149         if(odd==0){
    150             while(true){
    151                 int e=(*s.begin()).second;
    152                 if(shan(e)){
    153                     s.erase(s.begin());
    154                 }
    155                 else break;
    156             }
    157         }
    158         if(odd)puts("-1");
    159         else printf("%d
    ",-(*s.begin()).first);
    160     }
    161     return 0;
    162 }
    LCT

    2.CDQ分治

    solve(l,r,ll,rr)代表处理(l,r)的询问,且答案在(ll,rr)之内的情况,然后分治递归,可持久化堆。每次算出mid=(l+r)/2的答案nmid

    那么(l,mid-1)的答案在(nmid,r)之间,(mid+1,r)的答案在(l,nmid)之间,递归下去即可

    //start:   finish:
    #include<bits/stdc++.h>
    #define maxn 300005
    using namespace std;
    int n,m;
    int ans[maxn];
    struct DUI
    {
        int last,tot;
        int fa[maxn],sz[maxn];
        int p[maxn],q[maxn];
        int odd;
        int find(int x){
            if(fa[x]==x)return x;
            return find(fa[x]);
        }
        void bei(){
            last=tot;
        }
        void merge(int x,int y){
            if(find(x)==find(y))return;
            x=find(x);y=find(y);
            if(sz[x]%2==1 && sz[y]%2==1)odd-=2;
            if(sz[x]>sz[y])swap(x,y);
            fa[x]=y;sz[y]+=sz[x];
            p[++tot]=x;q[tot]=y;
        }
        void back(int mu)
        {
            while(tot>mu){
                int x=p[tot],y=q[tot];
                fa[x]=x;sz[y]-=sz[x];
                if(sz[x]%2==1 && sz[y]%2==1)odd+=2;
                tot--;
            }
        }
        
    }D;
    struct node
    {
        int x,y,z;
    }a[maxn];
    int p[maxn],rnk[maxn];
    bool cmp(int x,int y)
    {
        return a[x].z<a[y].z;
    }
    void solve(int l,int r,int ll,int rr){
        if(l>r)return;
        int tmp=D.tot;
        //cout<<l<<" "<<r<<" "<<a[p[ll]].z<<" "<<a[p[rr]].z<<" "<<D.tot<<endl;
        int mid=(l+r)/2;
        for(int i=l;i<=mid;i++)if(rnk[i]<ll)D.merge(a[i].x,a[i].y);
        int nmid=-1;
        for(int i=ll;i<=rr && D.odd;i++){
            if(p[i]<=mid){
                D.merge(a[p[i]].x,a[p[i]].y);
            }
            if(D.odd==0){
                nmid=i;
                break;
            }
        }
        D.back(tmp);
        if(nmid==-1){
            for(int i=l;i<=mid;i++)ans[i]=-1;
            for(int i=l;i<=mid;i++)if(rnk[i]<ll)D.merge(a[i].x,a[i].y);
            solve(mid+1,r,ll,rr);
            D.back(tmp);
            return;
        }
        ans[mid]=a[p[nmid]].z;
        for(int i=ll;i<nmid;i++){
            if(p[i]<l)D.merge(a[p[i]].x,a[p[i]].y);
        }
        solve(l,mid-1,nmid,rr);
        D.back(tmp);
        for(int i=l;i<=mid;i++)if(rnk[i]<ll)D.merge(a[i].x,a[i].y);
        solve(mid+1,r,ll,nmid);
        D.back(tmp);
    }
    int main()
    {
    //    freopen("1.in","r",stdin);
    //    freopen("1.out","w",stdout);
        scanf("%d%d",&n,&m);
        D.odd=n;
        for(int i=1;i<=n;i++)D.fa[i]=i,D.sz[i]=1;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
            p[i]=i;
        }
        sort(p+1,p+m+1,cmp);
        for(int i=1;i<=m;i++)rnk[p[i]]=i;
        solve(1,m,1,m);
        for(int i=1;i<=m;i++)
        {
            printf("%d
    ",ans[i]);
        }
        return 0;
    }
    /*
    4 10
    2 1 999999029
    3 1 999999800
    4 2 999999466
    1 3 9961
    1 3 9960
    2 4 9943
    3 1 9938
    1 3 9923
    4 2 9917
    3 4 9903
    
    3 4 9890
    4 1 9884
    1 3 9880
    2 1 9862
    1 2 9856
    */
    CDQ分治

    3.线段树做法

    通过上一个方法,就可以容易发现,如果一条边在第i时间在答案中,那么它从出生开始到i都在答案中,于是用线段树维护每个时间有哪些边在,然后更新答案,线段树用来存储这一段区间里必有哪些边,依然需要可持久化堆。

    //start:   finish:
    #include<bits/stdc++.h>
    #define maxn 1600005
    using namespace std;
    int n,m;
    int ans[maxn];
    struct node{
        int x,y,z;
    }a[maxn];
    int p[maxn],now;
    vector <int> v[maxn];
    bool cmp(int x,int y)
    {
        return a[x].z<a[y].z;
    }
    struct DUI
    {
        int last,tot;
        int fa[maxn],sz[maxn];
        int p[maxn],q[maxn];
        int odd;
        int find(int x){
            if(fa[x]==x)return x;
            return find(fa[x]);
        }
        void bei(){
            last=tot;
        }
        void merge(int x,int y){
            if(find(x)==find(y))return;
            x=find(x);y=find(y);
            if(sz[x]%2==1 && sz[y]%2==1)odd-=2;
            if(sz[x]>sz[y])swap(x,y);
            fa[x]=y;sz[y]+=sz[x];
            p[++tot]=x;q[tot]=y;
        }
        void back(int mu)
        {
            while(tot>mu){
                int x=p[tot],y=q[tot];
                fa[x]=x;sz[y]-=sz[x];
                if(sz[x]%2==1 && sz[y]%2==1)odd+=2;
                tot--;
            }
        }
        
    }D;
    void modify(int x,int l,int r,int tx,int ty,int wh){
        if(l>=tx && r<=ty){
            v[x].push_back(wh);
            return;
        }
        int mid=(l+r)/2;
        if(mid>=tx)modify(x*2,l,mid,tx,ty,wh);
        if(mid<ty)modify(x*2+1,mid+1,r,tx,ty,wh);
    }
    void solve(int x,int l,int r){
        int tmp=D.tot;
    //    cout<<x<<" "<<l<<" "<<r<<" "<<D.odd<<" "<<v[x].size()<<endl;
        for(int i=0;i<(int)v[x].size();i++)D.merge(a[v[x][i]].x,a[v[x][i]].y);
        if(l!=r){
            int mid=(l+r)/2;
            solve(x*2+1,mid+1,r);
            solve(x*2,l,mid);
        }
        else{
        //    cout<<D.odd<<endl;
            for(;now<=m && D.odd;now++){
                int id=p[now];
                if(id>l)continue;
                D.merge(a[id].x,a[id].y);
                if(id<l){
                //    cout<<id<<" "<<l-1<<" "<<id<<endl;
                    modify(1,1,m,id,l-1,id);
                }
            }
            if(D.odd)ans[l]=-1;else ans[l]=a[p[now-1]].z;
        }
        D.back(tmp);
    }
    int main(){
        scanf("%d%d",&n,&m);
        D.odd=n;
        for(int i=1;i<=n;i++){
            D.sz[i]=1;
            D.fa[i]=i;
        }
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
            p[i]=i;
        }
        sort(p+1,p+m+1,cmp);
        now=1;
        solve(1,1,m);
        for(int i=1;i<=m;i++){
            printf("%d
    ",ans[i]);
        }
        return 0;
    }
    Segment Tree

    小结:

    这三个方法都建立在前面的结论上,这个结论还是很强大的

    方法一:

    方法二:

    方法三:

    后面两种方法的代码长度和时间都比第一种短,但第一种最简单直观。

    个人还是非常喜欢这道题的。

  • 相关阅读:
    西藏之行的一点总结
    《吃的法则》总结
    《心法》总结
    分布式系统中的CAP、ACID、BASE概念
    《你当像鸟飞往你的山》总结
    《怪诞行为学》总结
    编程的一些抽象核心
    《活法》总结
    Mac使用Charles给iPhone抓包流程
    《认知天性》总结
  • 原文地址:https://www.cnblogs.com/sillygirl/p/6323306.html
Copyright © 2020-2023  润新知