【NOIP2017提高组模拟6.27】C

来源:互联网 发布:今日方知我是我的诗句 编辑:程序博客网 时间:2024/06/08 15:20

Description

蜘蛛精大爷是世界上最爷的爷,ta的图论专著《蜘蛛精大爷教你学做人OI之图论》正在热卖,只要233美元一本,每人限购一本。。。。。。在某弱的不懈要求下,ta给某弱出了一道题,然而某弱太弱了,只好向你求助。
给你一张n个结点,m条边的无向图,每个结点都有一个整数权值。你需要执行一系列操作。操作分为三种,如下表所示。
操作
备注
D x (1<=x<=m)
删除编号为x的边。输入保证每条边至多被删除一次。
Q x k (1<=x<=n)
计算出结点x所在的联通块中,第k大的权值。如果不存在,输出0。
C x v (1<=x<=n)
把结点x的权值改为v。
操作序列的结束标志为单个字母E。结点编号为1到n,边的编号为1到m。

Input

输入第一行为两个整数n和m。
接下来n行每行一个整数,表示个节点的初始权值。
接下来m行每行两个整数,表示一条边的两个端点。
接下来是各条指令,以单个字母E结尾。

Output

对于每个Q指令,输出一行,包括一个计算结果。

Sample Input

3 3
10
20
30
1 2
2 3
1 3
D 3
Q 1 2
Q 2 1
D 2
Q 3 2
C 1 50
Q 1 1
E

Sample Output

20
30
0
50

Data Constraint

对于前50%的数据,n <= 1000,m <= 5000。
对于前10%的数据,没有D指令且没有C指令。
对于另20%的数据,没有D指令。
对于另20%的数据,没有C指令。
对于100%的数据,1 <= n <= 20000,0 <= m <= 60000,每个结点的权值为绝对值不超过
的整数,Q指令和C指令不超过200000条。

Solution

这题的数据范围没有完全给定,这就使得容易RE
首先把所有点的权值都看成正的,就是加上一个比较大的常数,输出时减掉即可
存有多少个数并且要求出第K大,用权值线段树比较方便,splay也可以
而分解线段树我没听说过,所以就反过来做,线段树合并

那么就很简单了,稍微细心码一码,注意好细节,这种题小数据对了大数据也不会错

Code

#include<cstdio>#include<algorithm>#include<cstring>#define fo(i,a,b) for(int i=a;i<=b;i++)#define N 20100#define INF 2147483647ll#define ll long longusing namespace std;struct node{    int l,r,d;}t[N*100];int ans[N*10],n,m,r[N],edg[N*3][2],bz[N*3],q[N*100][3],fa[N],tot,c[N];void change(int v,ll i,ll j,ll x,int z){    t[v].d+=z;    if(i==j) return;    ll m=(i+j)/2;    if(x<=m) t[v].l=t[v].l==0?++tot:t[v].l,change(t[v].l,i,m,x,z);    else t[v].r=t[v].r==0?++tot:t[v].r,change(t[v].r,m+1,j,x,z);}ll get(int v,ll i,ll j,int x){    if(i==j) return i;    if(t[v].d<x) return INF;    ll m=(i+j)/2;    if(t[t[v].r].d>=x) return get(t[v].r,m+1,j,x);    else return get(t[v].l,i,m,x-t[t[v].r].d);}void hb(int v1,int v2,ll i,ll j){    if(i==j) {t[v1].d+=t[v2].d;return;}    ll m=(i+j)/2;    if(t[t[v1].l].d==0) t[v1].l=t[v2].l;    else if(t[t[v2].l].d!=0) hb(t[v1].l,t[v2].l,i,m);    if(t[t[v1].r].d==0) t[v1].r=t[v2].r;    else if(t[t[v2].r].d!=0) hb(t[v1].r,t[v2].r,m+1,j);    t[v1].d=t[t[v1].l].d+t[t[v1].r].d;}int gf(int x){return fa[x]==0?x:fa[x]=gf(fa[x]);}void he(int x,int y){    x=gf(x),y=gf(y);    if(x==y) return;    fa[y]=x;    hb(r[x],r[y],1,INF+INF);}int main(){    scanf("%d%d",&n,&m);    fo(i,1,n) scanf("%d",&c[i]);    fo(i,1,m) scanf("%d%d\n",&edg[i][0],&edg[i][1]);    int i=0;    while(1)    {        char ch;scanf("%c",&ch);        if(ch=='E') break;        i++;        if(ch=='D') q[i][0]=0,scanf("%d\n",&q[i][1]),bz[q[i][1]]=1;        if(ch=='C') q[i][0]=1,scanf("%d%d\n",&q[i][1],&q[i][2]),swap(c[q[i][1]],q[i][2]);        if(ch=='Q') q[i][0]=2,scanf("%d%d\n",&q[i][1],&q[i][2]),ans[0]++;    }    fo(i,1,n) r[i]=++tot,change(r[i],1,INF+INF,c[i]+INF,1);    fo(i,1,m) if(bz[i]==0) he(edg[i][0],edg[i][1]);    int at=ans[0];    for(;i;i--)    {        if(q[i][0]==0) he(edg[q[i][1]][0],edg[q[i][1]][1]);        if(q[i][0]==1)        {            int rt=r[gf(q[i][1])];            change(rt,1,INF+INF,c[q[i][1]]+INF,-1);            change(rt,1,INF+INF,q[i][2]+INF,1);            c[q[i][1]]=q[i][2];        }        if(q[i][0]==2) ans[ans[0]--]=(int)(get(r[gf(q[i][1])],1,INF+INF,q[i][2])-INF);    }    fo(i,1,at) printf("%d\n",ans[i]);}