4415: [Shoi2013]发牌|线段树|并查集

来源:互联网 发布:seo效果明显吗 编辑:程序博客网 时间:2024/06/05 01:34

很容易发现,如果有n张牌经过销牌n次的操作后与原牌的顺序是相同的
然后销牌的操作就相当于从当前的牌开始一直向后扫,扫到n后再回到1,直到找到第Ri张牌
这样可以用线段树维护一下,看当前销完牌后所取得牌在now...n还是1...(now1)直接用线段树找出这张牌。用并查集维护去玩这张牌后的下一张牌
复杂度O(nlog2n)树状数组常数会小一点..但是我忘了树状数组怎么找第k个数QAQ..

#include<algorithm>#include<iostream>#include<cstdlib>#include<cstring>#include<vector>#include<cstdio>#include<queue>#include<cmath>#include<set>#include<map>#define N 700005#define L x<<1#define R x<<1|1using namespace std;int sc(){    int i=0; char c=getchar();    while(c>'9'||c<'0')c=getchar();    while(c>='0'&&c<='9')i=i*10+c-'0',c=getchar();    return i;}int t[N*4],fa[N],n,now=1;int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}void build(int x,int l,int r){    t[x]=r-l+1;    if(l==r)return;    int mid=l+r>>1;    build(L,l,mid),build(R,mid+1,r);}int query(int x,int l,int r,int p){    if(p<=1)return 0;    if(r<p)return t[x];    int mid=l+r>>1,ans=0;    ans+=query(L,l,mid,p);    if(p>mid+1)ans+=query(R,mid+1,r,p);    return ans;}int ask(int x,int l,int r,int p){    if(l==r)return l;    int v=t[L],mid=l+r>>1;    if(p<=v)return ask(L,l,mid,p);    else return ask(R,mid+1,r,p-v);}void change(int x,int l,int r,int p){    t[x]--;if(l==r)return;    int mid=l+r>>1;    if(p<=mid)        change(L,l,mid,p);    else        change(R,mid+1,r,p);}int main(){    n=sc();    for(int i=1;i<=n;i++)fa[i]=i;    build(1,1,n);    for(int i=n;i;i--)    {        int x=sc()%i,p;        int sum=query(1,1,n,now);        if(x>=i-sum)             x=x-(i-sum)+1;        else            x=x+sum+1;        p=ask(1,1,n,x);        change(1,1,n,p);        fa[p]=(p==n?1:p+1);        if(i!=1)now=find(p);        printf("%d\n",p);    }    return 0;}
0 0