【NOIP2017提高组模拟6.27】C

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 long
using 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]);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值