<span style="font-family: SimHei;">#include<cstdio></span>
#include<cstdlib> #include<algorithm> #include<vector> #include<cstring> #include<iostream> #include<queue> #include<cmath> using namespace std;//题目说每种询问20万,总共60万,开小了RE const int MAXN=20005,MAXM=60005,MAXQ=600001,MAXNODE=MAXQ*2; int val[MAXNODE],r[MAXNODE],w[MAXM],u[MAXM],v[MAXM],ch[MAXNODE][2],fa[MAXN]; int size[MAXNODE],root[MAXNODE],vis[MAXM],n,m; long long ans=0;int cnt=0; queue<int> que; struct Query { char c;int a,b; Query(char c=0,int a=0,int b=0):c(c),a(a),b(b){} void init(){c=0;a=0;b=0;} }c[MAXQ]; void Read(int &x) { x=0;char c;int flag=0,si=1; while(c=getchar()) { if(c=='-')si=-1; if(c>='0'&&c<='9') x*=10,x+=c-'0',flag=1; else if(flag) break; } x*=si; } void init() { memset(vis,0,sizeof(vis)); memset(size,0,sizeof(size)); for(int i=1;i<=MAXQ;i++)c[i].init();//这里没有清空结构体导致多组数据WA memset(ch,0,sizeof(ch)); for(int i=1;i<=n;i++) Read(w[i]); for(int i=1;i<=m;i++) Read(u[i]),Read(v[i]); for(int i=1;i<=n;i++)fa[i]=i; while(!que.empty())que.pop(); for(int i=1;i<MAXNODE;i++)que.push(i); } int newnode(int vv) { int sz=que.front();que.pop(); val[sz]=vv;r[sz]=rand();size[sz]=1; return sz; } int cmp(int a,int b)//我本来写的是bool,sort写多了。。。导致返回-1的时候直接返回1 { if(b==val[a])return -1; return b<val[a]?0:1; } void maintain(int x) { size[x]=size[ch[x][0]]+size[ch[x][1]]+1; } void rotate(int &o,int d) { int k=ch[o][d^1];ch[o][d^1]=ch[k][d];ch[k][d]=o;//这里d直接用1,0代替了 maintain(o),maintain(k);o=k; } void del(int &x) { que.push(x);x=0; } void insert(int &o,int x) { if(!o){o=newnode(x);return ;} int d=x<val[o]?0:1; insert(ch[o][d],x); if(r[o]<r[ch[o][d]])rotate(o,d^1); maintain(o); } void remove(int &o,int x) { int d=cmp(o,x); if(d==-1) { if(ch[o][0]==0)o=ch[o][1]; else if(ch[o][1]==0)o=ch[o][0]; else{ int d2=r[ch[o][0]]>r[ch[o][1]]?1:0; rotate(o,d2);remove(ch[o][d2],x); } } else remove(ch[o][d],x); if(o)maintain(o);//并没有maintain } int getFa(int x) { return fa[x]==x?x:fa[x]=getFa(fa[x]); } void merge(int &a,int &b) { if(ch[a][0])merge(ch[a][0],b); if(ch[a][1])merge(ch[a][1],b); insert(b,val[a]);del(a); } int kth(int &o,int x) { if(x>size[o]||!o||x<=0)return 0; int num=size[ch[o][1]]; if(num+1==x)return val[o]; if(x<=num)return kth(ch[o][1],x); else return kth(ch[o][0],x-num-1);//num,x写反了导致出了负数 } void chang(int x,int y) { int u=getFa(x); remove(root[u],w[x]); insert(root[u],w[x]=y); } void addEge(int i) { int f1=getFa(u[i]),f2=getFa(v[i]); if(f1==f2)return; if(size[f1]>size[f2])swap(f1,f2); fa[f1]=f2; merge(root[f1],root[f2]); } int main() { freopen("C.in","r",stdin); freopen("C.out","w",stdout); int kase=0; while(1) { Read(n);Read(m); if(n==0&&m==0)break; init(); int q=1,cnt=0; char C; int x,y;long long ans=0; while(1) { scanf("%c",&C); if(C=='E')break; if(C=='Q') Read(x),Read(y); else if(C=='D') Read(x),y=0,vis[x]=1; else if(C=='C')Read(x),y=w[x],Read(w[x]);//这里没有倒着存 if(C=='D'||C=='Q'||C=='C')c[q++]=Query(C,x,y); } for(int i=1;i<=n;i++) root[i]=newnode(w[i]); for(int i=1;i<=m;i++) if(!vis[i])addEge(i); while(q>=1) { C=c[q].c;x=c[q].a;y=c[q].b; if(C=='D')addEge(x); else if(C=='C')chang(x,y); else if(C=='Q')ans+=(long long)kth(root[getFa(x)],y),cnt++; q--; } printf("Case %d: %.6lf\n",++kase,(double)ans/cnt); } }
这是我们联赛模拟的第三题,我打了个暴力,然而出题人的数据根本不给暴力留活路,无奈看着std然后打了个treap。
给定一张无向图,有三个操作:删除一条边,修改一个点的权值,查询一个点所在联通块的K大值。。这题可以设计一个离线的算法,也就是说先把所有操作读进来,然后删除掉的边标记一下,建成最终的图(其实并不需要真正建图,用并查集维护联通性即可),最后反着做,也就是加边。我们给每一个联通块建一treap,记一个root维护每个点所在treap的根结点的编号(这个编号是树上节点编号,不是输入的图节点的编号)修改权值就删除这个,再插入这个,我当时没想到修改也要倒着来,也就是说w数组应该保存最后的修改后的权值,修改权值时再一步一步改回,于是光荣WA了,调了好久。加边的话实际上就是合并两个treap,我们采用启发式合并,即size小的往size大的一个一个暴力插入,每次合并,任意一个需要插入的点所在树的size至少会加倍。由于树的节点不超过n,任意节点至多插logn次,每次插要logn,因此总时间复杂度是O(nlog^2 n)的,实际上应该快得多。
然后就是放代码了,代码里有我WA过的地方。
%%%__debug大神打splay三百多行。。。