2733: [HNOI2012]永无乡
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1321 Solved: 693
[ Submit][ Status]
Description
永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示。某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛。如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的。现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥。Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k 小的岛是哪 座,请你输出那个岛的编号。
Input
输入文件第一行是用空格隔开的两个正整数 n 和 m,分别 表示岛的个数以及一开始存在的桥数。接下来的一行是用空格隔开的 n 个数,依次描述从岛 1 到岛 n 的重要度排名。随后的 m 行每行是用空格隔开的两个正整数 ai 和 bi,表示一开始就存 在一座连接岛 ai 和岛 bi 的桥。后面剩下的部分描述操作,该部分的第一行是一个正整数 q, 表示一共有 q 个操作,接下来的 q 行依次描述每个操作,操作的格式如上所述,以大写字母 Q 或B 开始,后面跟两个不超过 n 的正整数,字母与数字以及两个数字之间用空格隔开。 对于 20%的数据 n≤1000,q≤1000
对于 100%的数据 n≤100000,m≤n,q≤300000
Output
对于每个 Q x k 操作都要依次输出一行,其中包含一个整数,表 示所询问岛屿的编号。如果该岛屿不存在,则输出-1。
Sample Input
4 3 2 5 1
1 2
7
Q 3 2
Q 2 1
B 2 3
B 1 5
Q 2 1
Q 2 4
Q 2 3
Sample Output
2
5
1
2
/**************************************************************
Problem: 2733
User: _vampire_
Language: C++
Result: Accepted
Time:2244 ms
Memory:7480 kb
****************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
int n,m,q,fa[100001],a[100001],b[100001];
struct Node
{
Node*ch[2];
int r,v,s;
Node(int v):v(v) {r=rand(); ch[0]=ch[1]=NULL;}
int cmp(int x) const{
if(x==v) return -1;
else return (x>v?1:0);
}
void Maintain(){
s=1;
if(ch[0]!=NULL) s+=ch[0]->s;
if(ch[1]!=NULL) s+=ch[1]->s;
}
}*root[100001];
inline int find(int x)
{
if(fa[x]!=x) fa[x]=find(fa[x]);
return fa[x];
}
inline void rotate(Node* &o,int d)
{
Node* k=o->ch[d^1]; o->ch[d^1]=k->ch[d]; k->ch[d]=o;
o->Maintain(); k->Maintain(); o=k;
}
inline void insert(Node* &o,int x)
{
if(o==NULL) o=new Node(x);
else
{
int d=o->cmp(x);
insert(o->ch[d],x);
if(o->r < o->ch[d]->r) rotate(o,d^1);
}
o->Maintain();
}
inline void work(Node* &o1,Node* &o2)
{
if(o2->ch[0]!=NULL) work(o1,o2->ch[0]);
if(o2->ch[1]!=NULL) work(o1,o2->ch[1]);
insert(o1,o2->v);
}
inline void Merge(int x,int y)
{
if(root[x]->s>root[y]->s) work(root[x],root[y]), fa[y]=x;
else work(root[y],root[x]), fa[x]=y;
}
inline int kth(Node* &o,int x)
{
if(o==NULL||x>o->s||x<=0) return 0;
int d=(o->ch[0]==NULL?0:o->ch[0]->s);
if(x==d+1) return o->v;
if(x<=d) return kth(o->ch[0],x);
else return kth(o->ch[1],x-d-1);
}
int main()
{
int i,j,x,y,r1,r2,xx;
char aa;
scanf("%d%d",&n,&m);
for(i=1;i<=n;++i)
{
scanf("%d",&a[i]);
fa[i]=i; b[a[i]]=i;
insert(root[i],a[i]);
}
for(i=1;i<=m;++i)
{
scanf("%d%d",&x,&y);
r1=find(x); r2=find(y);
if(r1!=r2) Merge(r1,r2);
}
scanf("%d",&q);
for(i=1;i<=q;++i)
{
scanf("%*c%s%d%d",&aa,&x,&y);
if(aa=='B')
{
r1=find(x); r2=find(y);
if(r1!=r2) Merge(r1,r2);
}
else
{
xx=find(x);
if(root[xx]->s<y) printf("-1\n");
else
{
xx=kth(root[xx],y);
printf("%d\n",b[xx]);
}
}
}
}
1503: [NOI2004]郁闷的出纳员
Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 6841 Solved: 2395
[ Submit][ Status]
Description
OIER公司是一家大型专业化软件公司,有着数以万计的员工。作为一名出纳员,我的任务之一便是统计每位员工的工资。这本来是一份不错的工作,但是令人郁闷的是,我们的老板反复无常,经常调整员工的工资。如果他心情好,就可能把每位员工的工资加上一个相同的量。反之,如果心情不好,就可能把他们的工资扣除一个相同的量。我真不知道除了调工资他还做什么其它事情。工资的频繁调整很让员工反感,尤其是集体扣除工资的时候,一旦某位员工发现自己的工资已经低于了合同规定的工资下界,他就会立刻气愤地离开公司,并且再也不会回来了。每位员工的工资下界都是统一规定的。每当一个人离开公司,我就要从电脑中把他的工资档案删去,同样,每当公司招聘了一位新员工,我就得为他新建一个工资档案。老板经常到我这边来询问工资情况,他并不问具体某位员工的工资情况,而是问现在工资第k多的员工拿多少工资。每当这时,我就不得不对数万个员工进行一次漫长的排序,然后告诉他答案。好了,现在你已经对我的工作了解不少了。正如你猜的那样,我想请你编一个工资统计程序。怎么样,不是很困难吧?
Input
Output
输出文件的行数为F命令的条数加一。对于每条F命令,你的程序要输出一行,仅包含一个整数,为当前工资第k多的员工所拿的工资数,如果k大于目前员工的数目,则输出-1。输出文件的最后一行包含一个整数,为离开公司的员工的总数。
Sample Input
I 60
I 70
S 50
F 2
I 30
S 15
A 5
F 1
F 2
Sample Output
20
-1
2
HINT
I命令的条数不超过100000 A命令和S命令的总条数不超过100 F命令的条数不超过100000 每次工资调整的调整量不超过1000 新员工的工资不超过100000
/**************************************************************
Problem: 1503
User: _vampire_
Language: C++
Result: Accepted
Time:708 ms
Memory:3260 kb
****************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
using namespace std;
int n,k;
struct Node
{
Node*ch[2];
int r,v,num,s;
Node(int v):v(v){ch[0]=ch[1]=NULL; r=rand(); num=1;};
int cmp(int x) const {
if(x==v) return -1;
else return (x>v?1:0);
}
void Maintain(){
s=num;
if(ch[0]!=NULL) s+=ch[0]->s;
if(ch[1]!=NULL) s+=ch[1]->s;
}
};
Node* root;
inline void rotate(Node* &o,int d)
{
Node* k=o->ch[d^1]; o->ch[d^1]=k->ch[d]; k->ch[d]=o;
o->Maintain(); k->Maintain(); o=k;
}
inline void insert(Node* &o,int x)
{
if(o==NULL) o=new Node(x);
else
{
int d=o->cmp(x);
if(d==-1) o->num+=1;
else
{
insert(o->ch[d],x);
if(o->r < o->ch[d]->r) rotate(o,d^1);
}
}
o->Maintain();
}
inline int rank(Node* &o,int x)
{
int d=o->cmp(x);
if(d==-1) return (o->ch[0]==NULL?1:o->ch[0]->s+1);
else
{
if(d==0) return rank(o->ch[0],x);
else return (rank(o->ch[1],x)+(o->ch[0]==NULL?o->num:o->ch[0]->s+o->num));
}
}
inline void del(Node* &o,int x)
{
int d=o->cmp(x);
if(d==-1)
{
if(o->num==1)
{
if(o->ch[0]==NULL) o=o->ch[1];
else if(o->ch[1]==NULL) o=o->ch[0];
else
{
int d2=(o->ch[0]->r>o->ch[1]->r?1:0);
rotate(o,d2); del(o->ch[d2],x);
}
}
else o->num-=1;
}
else del(o->ch[d],x);
if(o!=NULL) o->Maintain();
}
inline int kth(Node* &o,int x)
{
if(o==NULL||o->s<x||x<=0) return 0;
int d=(o->ch[0]==NULL?0:o->ch[0]->s);
if(d+1<=x&&x<=d+o->num) return o->v;
if(x<=d) return kth(o->ch[0],x);
else return kth(o->ch[1],x-d-o->num);
}
int main()
{
int i,j,x,now=0,ans=0,sum=0,y,t;
char a;
scanf("%d%d",&n,&k);
for(i=1;i<=n;++i)
{
scanf("%*c%s%d",&a,&x);
if(a=='I')
{
if(x+now>=k)
{
insert(root,x+now);
sum+=1;
}
}
if(a=='A') now-=x,k-=x;
if(a=='S')
{
now+=x; k+=x;
insert(root,k); t=rank(root,k);
if(t>1)
{
ans+=t-1; sum=sum-t+1;
for(j=1;j<=t-1;++j)
{
y=kth(root,1); del(root,y);
}
}
del(root,k);
}
if(a=='F')
{
if(x>sum) printf("-1\n");
else printf("%d\n",kth(root,sum-x+1)-now);
}
}
printf("%d\n",ans);
}
模板题 :http://cojs.tk/cogs/problem/problem.php?pid=1829
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#define MM 2100000000
using namespace std;
int n,ans;
struct Node
{
Node*ch[2];
int v,r,s,num;
Node(int v):v(v) { ch[0]=ch[1]=NULL; num=1; r=rand();}
int cmp(int x) const {
if(x==v) return -1;
else return (x>v?1:0);
}
void Maintain(){
s=num;
if(ch[0]!=NULL) s+=ch[0]->s;
if(ch[1]!=NULL) s+=ch[1]->s;
}
};
Node* root;
inline void rotata(Node* &o,int d)
{
Node* k=o->ch[d^1]; o->ch[d^1]=k->ch[d]; k->ch[d]=o;
o->Maintain(); k->Maintain(); o=k;
}
inline void insert(Node* &o,int x)
{
if(o==NULL) o=new Node(x);
else
{
int d=o->cmp(x);
if(d==-1) o->num+=1;
else
{
insert(o->ch[d],x);
if(o->r<o->ch[d]->r) rotata(o,d^1);
}
}
o->Maintain();
}
inline void del(Node* &o,int x)
{
int d=o->cmp(x);
if(d==-1)
{
if(o->num==1)
{
if(o->ch[0]==NULL) o=o->ch[1];
else if(o->ch[1]==NULL) o=o->ch[0];
else
{
int d2=(o->ch[1]->r > o->ch[0]->r?0:1);
rotata(o,d2); del(o->ch[d2],x);
}
}
else o->num-=1;
}
else del(o->ch[d],x);
if(o!=NULL) o->Maintain();
}
inline int rank(Node* &o,int x)
{
int d=o->cmp(x);
if(d==-1) return (o->ch[0]==NULL?1:o->ch[0]->s+1);
else
{
if(d==0) return rank(o->ch[0],x);
else return (rank(o->ch[1],x)+(o->ch[0]==NULL?o->num:o->ch[0]->s+o->num));
}
}
inline int kth(Node* &o,int x)
{
if(o==NULL||o->s<x||x<=0) return 0;
int d=(o->ch[0]==NULL?0:o->ch[0]->s);
if(d+1<=x&&x<=d+o->num) return o->v;
if(x<=d) return kth(o->ch[0],x);
else return kth(o->ch[1],x-d-o->num);
}
inline void pre(Node* &o,int x)
{
if(x<=o->v)
{
if(o->ch[0]!=NULL) pre(o->ch[0],x);
}
else
{
ans=max(ans,o->v);
if(o->ch[1]!=NULL) pre(o->ch[1],x);
}
}
inline void sub(Node* &o,int x)
{
if(x>=o->v)
{
if(o->ch[1]!=NULL) sub(o->ch[1],x);
}
else
{
ans=min(ans,o->v);
if(o->ch[0]!=NULL) sub(o->ch[0],x);
}
}
int main()
{
freopen("phs.in","r",stdin);
freopen("phs.out","w",stdout);
int i,j,x,t;
scanf("%d",&n);
for(i=1;i<=n;++i)
{
scanf("%d%d",&t,&x);
if(t==1) insert(root,x);
if(t==2) del(root,x);
if(t==3) printf("%d\n",rank(root,x));
if(t==4) printf("%d\n",kth(root,x));
if(t==5)
{
ans=-MM; pre(root,x);
printf("%d\n",ans);
}
if(t==6)
{
ans=MM; sub(root,x);
printf("%d\n",ans);
}
}
}
①:因为我的treap用到的是指针操作,所以有些地方写残了就很容易炸,在写代码的时候要更加严谨,多考虑和判断一些情况。
②:第二点就是在做有些难度稍大的题的时候,我们往往可以把这个题分解成之前学过的一些算法和结构的形式,这样我们就可以把一道看似有些难度的变成一道比较简单的题了。