• bzoj 3514: Codechef MARCH14 GERALD07加强版


    Description

    N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数。

    Input

    第一行四个整数N、M、K、type,代表点数、边数、询问数以及询问是否加密。
    接下来M行,代表图中的每条边。
    接下来K行,每行两个整数L、R代表一组询问。对于type=0的测试点,读入的L和R即为询问的L、R;对于type=1的测试点,每组询问的L、R应为L xor lastans和R xor lastans。

    Output

     K行每行一个整数代表该组询问的联通块个数。

    Sample Input

    3 5 4 0
    1 3
    1 2
    2 1
    3 2
    2 2
    2 3
    1 5
    5 5
    1 2

    Sample Output

    2
    1
    3
    1

    HINT

    对于100%的数据,1≤N、M、K≤200,000。

    2016.2.26提高时限至60s

    Source

    By zhonghaoxi

    一开始看到这个题的时候第一眼想法就是暴力莫队+LCT,然而是强制在线QAQ。。。

    然后就不会了,然后就颓了。。。根本想不到这题跟主席树有什么关系。。。

    看了题解之后膝盖都给跪烂,Orz%%%

    做法是这样的:

    不断加边,如果产生了环,那么把环上编号最小的边给删掉,并且用ntr[i]记录该边把哪条边给删掉了。。。如果没删掉就是0;

    对于每个l-r区间的询问查找这些边有多少边的ntr[i]<l(num),对于每个询问的答案为n-num;

    这样为什么是对的呢???

    对于一条边,如果ntr[i]>=l,那么他一定可以与l--r这个区间的边产生一个环。。。环的话对于连通块的数量是没有贡献的。

    反之,如果一条边的ntr[i]<l,那么他肯定不可能与l--r这个区间中的边产生环,也就相当于这条边对于l--r这个区间是一条树边,而每一条树边的贡献相当于是减少一个连通块。。。

    所以这样是有正确性的。。。

    加边删边是可以用LCT实现的,注意使用把边当做点的技巧。。。

    而查询区间中用小于l的边有多少个是可以用主席树实现的。。。

    以下附代码:

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<algorithm>
      4 #include<cstring>
      5 using namespace std;
      6 const int N=400050;
      7 const int INF=2147483647;
      8 int gi()
      9 {
     10     int x=0;
     11     char ch=getchar();
     12     while(ch<'0'||ch>'9') ch=getchar();
     13     while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
     14     return x;
     15 }
     16 int c[N][2],fa[N],minm[N],st[N],rev[N],root[N*20],s[N*20],sz,ls[N*20],rs[N*20],w[N];
     17 int n,m,k,type,ntr[N*2],last;
     18 struct ac
     19 {
     20     int x,y,id;
     21 }e[N];
     22 bool isroot(int x){return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;}
     23 void update(int x)
     24 {
     25     int l=c[x][0],r=c[x][1];
     26     minm[x]=x;
     27     if(w[minm[x]]>w[minm[l]]) minm[x]=minm[l];
     28     if(w[minm[x]]>w[minm[r]]) minm[x]=minm[r];
     29 }
     30 void pushdown(int x)
     31 {
     32     int l=c[x][0],r=c[x][1];
     33     if(rev[x])
     34     {
     35         rev[x]^=1;rev[l]^=1;rev[r]^=1;
     36         swap(c[x][0],c[x][1]);
     37     }
     38 }
     39 void rotate(int x)
     40 {
     41     int y=fa[x],z=fa[y],l,r;
     42     if(c[y][0]==x) l=0;else l=1;r=l^1;
     43     if(!isroot(y))
     44     {
     45         if(c[z][0]==y) c[z][0]=x;
     46         else c[z][1]=x;
     47     }
     48     fa[y]=x;fa[x]=z;fa[c[x][r]]=y;
     49     c[y][l]=c[x][r];c[x][r]=y;
     50     update(y);update(x);
     51 }
     52 void splay(int x)
     53 {
     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--) pushdown(st[i]);
     57     while(!isroot(x))
     58     {
     59         int y=fa[x],z=fa[y];
     60         if(!isroot(y))
     61         {
     62             if(c[y][0]==x^c[z][0]==y) rotate(x);
     63             else rotate(y);
     64         }
     65         rotate(x);
     66     }
     67 }
     68 void access(int x)
     69 {
     70     int t=0;
     71     while(x)
     72     {
     73         splay(x);
     74         c[x][1]=t;
     75         t=x;update(x);x=fa[x];
     76     }
     77 }
     78 void rever(int x) {access(x),splay(x),rev[x]^=1;}
     79 void lnk(int x,int y) {rever(x);fa[x]=y;}
     80 void cut(int x,int y) {rever(x);access(y);splay(y);c[y][0]=fa[x]=0;}
     81 int query1(int x,int y) {rever(x);access(y);splay(y);return minm[y];}
     82 int find(int x)
     83 {
     84     access(x);splay(x);
     85     while(c[x][0])x=c[x][0];
     86     return x;
     87 }
     88 void insert(int l,int r,int x,int &y,int v)
     89 {
     90     y=++sz;s[y]=s[x]+1;
     91     ls[y]=ls[x];rs[y]=rs[x];
     92     if(l==r) return;
     93     int mid=(l+r)>>1;
     94     if(v<=mid) insert(l,mid,ls[x],ls[y],v);
     95     else insert(mid+1,r,rs[x],rs[y],v);
     96 }
     97 int query(int l,int r,int x,int y,int v)
     98 {
     99     if(l==r) return s[y]-s[x];
    100     int mid=(l+r)>>1;
    101     if(v<=mid) return query(l,mid,ls[x],ls[y],v);
    102     else return s[ls[y]]-s[ls[x]]+query(mid+1,r,rs[x],rs[y],v);
    103 }
    104 void pre()
    105 {
    106     for(int i=1;i<=m;i++)
    107     {
    108         if(e[i].x==e[i].y) {ntr[i]=i;continue;}
    109         if(find(e[i].x)!=find(e[i].y)) 
    110         {
    111             w[n+i]=n+i;minm[n+i]=n+i;
    112             lnk(n+i,e[i].x);lnk(n+i,e[i].y);
    113         }
    114         else if(find(e[i].x)==find(e[i].y))
    115         {
    116             int mm=query1(e[i].x,e[i].y);ntr[i]=mm-n;
    117             cut(e[mm-n].x,mm);cut(e[mm-n].y,mm);
    118             w[n+i]=i+n;minm[n+i]=n+i;
    119             lnk(e[i].x,n+i);lnk(e[i].y,n+i);
    120         }
    121     }
    122     for(int i=1;i<=m;i++) insert(0,m,root[i-1],root[i],ntr[i]);
    123 }
    124 void work()
    125 {
    126     int l,r;
    127     for(int i=1;i<=k;i++)
    128     {
    129         l=gi();r=gi();
    130         if(type) l^=last,r^=last;
    131         last=n-query(0,m,root[l-1],root[r],l-1);
    132         printf("%d
    ",last);
    133     }
    134 }
    135 int main()
    136 {
    137   n=gi();m=gi();k=gi();type=gi();
    138   for(int i=1;i<=m;i++) e[i].x=gi(),e[i].y=gi(),e[i].id=i;
    139   w[0]=INF;
    140   for(int i=1;i<=n;i++) w[i]=INF,minm[i]=i;
    141   pre();
    142   work();
    143   return 0;
    144 }
  • 相关阅读:
    根据数组对象中的某个属性值排序
    vue小知识
    vue项目中config文件中的 index.js 配置
    小问题
    原生无缝轮播
    webpack打包提交代码
    echarts
    面试问题
    MySql
    vue-router 跳转原理
  • 原文地址:https://www.cnblogs.com/qt666/p/6486396.html
Copyright © 2020-2023  润新知