zoukankan      html  css  js  c++  java
  • 2020牛客暑期多校训练营(第八场)A All-Star Game 题解

    题意:

    有 n 个球员, m 个球迷。球迷 a 会看 球员 x 的 比赛当且仅当:

    1)a 是球员 x 的粉丝

    2)a 和 b 都会去看 球员 y 的比赛,b 也会去看 x 的比赛。

    初始有 K 组粉丝关系,一共有 q 个修改,每次修改将 a 是否是 x 的粉丝取反,每次修改之后询问如果想让所有球迷看比赛最少让多少球员打比赛,无解输出 -1 。


    如果把粉丝关系看做边,很显然每一个有球迷在联通块只用出一个球员。所以我们只要判断是不是所有球迷都有球星要追以及有多少含有球迷的联通块。

    其实一开始想的是按照边的消失时间来建最大生成树,但是这道题里面有中途加边的操作,最大生成树是做不了的。我们考虑可撤销并查集。

    可撤销并查集并不能路径压缩,但是我们可以通过按秩合并来保证复杂度。我个人理解的就是让深度小的集合并到深度大的集合里,这样每次向上爬的复杂度可以保持最坏O(logn)。

    我们还要解决的一点是如果有链接两个集合的两条边,一个在[1~5]存在,一个在[3~10]存在,如何在删除第一条边的情况下保持第二条边的状态呢?

    我们就要用时间建线段树,每个节点用vector保存在这个区间所表示的时间段内存在的且不完全在他父节点所表示时间段存在的所有边。这样,在一个时间点存在的所有边的集合就是他和他的所有祖先的vector里存着的边。我们在线段树上跑一遍遍历就可以了。

      1 #include<iostream>
      2 #include<cstdlib>
      3 #include<cstdio>
      4 #include<cstring>
      5 #include<cmath>
      6 #include<algorithm>
      7 #include<map>
      8 #include<vector>
      9 #define N 200005
     10 using namespace std;
     11 int n,m,q,zz;
     12 int L[N*5];
     13 map<int,int> ma[N];
     14 struct dda{
     15     int x,y;
     16     int depx,depy;
     17     int ans,cnt;
     18 }da[N*5],st[N*5];
     19 struct no{
     20     int left,right,mid;
     21     vector<int> q1;
     22 }node[N*4];
     23 void build(int left,int right,int x)
     24 {
     25     node[x].left=left,node[x].right=right;
     26     if(left==right)
     27     {
     28         return;
     29     }
     30     int mid=(left+right)>>1;
     31     node[x].mid=mid;
     32     build(left,mid,x<<1);
     33     build(mid+1,right,x<<1|1);
     34 }
     35 void update(int left,int right,int x,int da)
     36 {
     37     if(node[x].left==left&&node[x].right==right)
     38     {
     39         node[x].q1.push_back(da);
     40         return;
     41     }
     42     int mid=node[x].mid;
     43     if(left>mid) update(left,right,x<<1|1,da);
     44     else if(right<=mid) update(left,right,x<<1,da);
     45     else update(left,mid,x<<1,da),update(mid+1,right,x<<1|1,da);
     46 }
     47 int fa[N*2],deep[N*2];
     48 int find(int x)
     49 {
     50     if(fa[x]==x)return x;
     51     return find(fa[x]);
     52 }
     53 int top,ans,cnt;
     54 void merge(int x,int y)
     55 {
     56     x=find(x),y=find(y);
     57     top++;
     58 //    cout<<x<<' '<<y<<' '<<deep[x]<<' '<<deep[y]<<endl;
     59     st[top].x=x;st[top].y=y;
     60     st[top].depx=deep[x],st[top].depy=deep[y];
     61     st[top].ans=ans,st[top].cnt=cnt;
     62     if(x==y)return;
     63     if(deep[y]==1) cnt--;
     64     if(deep[x]==1&&deep[y]==1) ans++;
     65     else if(deep[x]>1&&deep[y]>1) ans--;
     66     if(deep[x]<deep[y])swap(x,y);
     67     if(deep[x]==deep[y]) deep[x]++;
     68     fa[y]=x;
     69 }
     70 void del()
     71 {
     72     fa[st[top].x]=st[top].x,fa[st[top].y]=st[top].y;
     73     deep[st[top].x]=st[top].depx,deep[st[top].y]=st[top].depy;
     74     ans=st[top].ans;cnt=st[top].cnt;
     75     top--;
     76 }
     77 void work(int x)
     78 {
     79     int l=node[x].q1.size();
     80     int la=top;
     81     for(int i=0;i<l;i++)
     82     {
     83         merge(da[node[x].q1[i]].x,da[node[x].q1[i]].y);
     84     }
     85     if(node[x].left==node[x].right)
     86     {
     87     //    cout<<"dsasaadsaa   "<<node[x].left<<' '<<node[x].right<<' '<<cnt<<endl;
     88         if(cnt) printf("-1
    ");
     89         else printf("%d
    ",ans);
     90     }
     91     else
     92     {
     93         work(x<<1);
     94         work(x<<1|1);
     95     }
     96     while(top!=la)
     97     {
     98         del();
     99     }
    100 }
    101 int main()
    102 {
    103     scanf("%d%d%d",&n,&m,&q);
    104     for(int i=1;i<=n;i++)
    105     {
    106         int l;
    107         scanf("%d",&l);
    108         for(int j=1;j<=l;j++)
    109         {
    110             int x;
    111             scanf("%d",&x);
    112             zz++;
    113             ma[i][x+n]=zz;
    114             L[zz]=1;
    115             da[zz].x=i;
    116             da[zz].y=x+n;
    117         }
    118     }
    119     build(1,q,1);
    120     for(int i=1;i<=q;i++)
    121     {
    122         int x,y;
    123         scanf("%d%d",&x,&y);
    124         x+=n;
    125         if(!ma[y].count(x)||!L[ma[y][x]])
    126         {
    127             if(!ma[y].count(x))
    128             {
    129                 zz++;
    130                 ma[y][x]=zz;
    131                 L[zz]=i;
    132                 da[zz].x=y;
    133                 da[zz].y=x;
    134             }
    135             else
    136             {
    137                 L[ma[y][x]]=i;
    138             }
    139         }
    140         else
    141         {
    142             int tmp=ma[y][x];
    143             if(i!=1) update(L[tmp],i-1,1,tmp);
    144             L[tmp]=0;
    145         }
    146     }
    147     cnt=m;
    148     for(int i=1;i<=zz;i++)
    149     {
    150         if(L[i])
    151         {
    152             update(L[i],q,1,i);
    153         }
    154     }
    155     for(int i=1;i<=n+m;i++) fa[i]=i,deep[i]=1;
    156     work(1);
    157     return 0;
    158 }
    View Code
  • 相关阅读:
    剑指 Offer 46. 把数字翻译成字符串
    leedcode:27. 移除元素
    1052. 爱生气的书店老板(滑动窗口)
    剑指 Offer 56
    剑指 Offer 11. 旋转数组的最小数字(二分)
    1919年巴黎和会顾维钧英语演讲稿
    状语从句
    定语从句
    名词性从句
    并列句
  • 原文地址:https://www.cnblogs.com/liutianrui/p/13675691.html
Copyright © 2011-2022 走看看