2017.10.17 Codechef MARCH14 GERALD07加强版 失败总结

以前做这个题简直是噩梦的难度

有个很神的做法就是 利用最简联通形式来统计联通块

把一个要求的区间写成一颗等价的树,,就有了统一的标准

然后考虑怎么构造这棵树,看每次加入的边,如果已经联通,则考虑把这个环上最早加的边弹出,这样可以在区间往右移时保留尽量多的边。

然后记录哪一条边被这一条边顶替

 查询时对于一个区间,默认这个区间没有联通块   如果区间内弹出了区间内的边,则这一定是重复的边,和区间里的另一条边等价,就忽略这一条边

如果弹出了区间外的边,则在这个区间里这个边一定链接了两个联通块,联通块个数--

如果没有弹出边,则这一条边不与1~r的边矛盾,更不与l~r的边矛盾,所以联通块个数--


这样就可以用lct求出替代数组,用主席树求区间<l的数的个数,n-查询结果即是答案


注:

1、access不要忘了splay

2、边化为点时-n和两边link cut

3、查询和修改都在一颗主席树上时,注意区分赋值语句

4、主席树没有up

5、注意改用什么变量


码(1A(主席树+lct)的壮举):

#include<iostream>
#include<cstdio>
using namespace std;
#define N 200005
int v[N*40],op,tot,rt[N],ch2[N*40][2],ch[N<<1][2],ntr[N],q[N],z[N],a,b,c,lll,rrr,minn[N<<1],from[N<<1],val[N<<1],rev[N<<1],fu[N<<1],ans,i,j,n,m,k,type;
void gai(int last,int o,int l,int r)
{
if(op==0)   v[o]=v[last]+1;
    if(a<=l&&r<=b)
    {
        if(op==1)
{
    c+=v[o]-v[last];    
}
        return ;    
    }   
    int mid=(l+r)>>1;
    if(op==0)ch2[o][0]=ch2[last][0];
    if(op==0)ch2[o][1]=ch2[last][1];
    if(a<=mid)
    {
    if(op==0)   ch2[o][0]=++tot;
        gai(ch2[last][0],ch2[o][0],l,mid);          
    }
    if(b>mid)
    {
    if(op==0)   ch2[o][1]=++tot;
        gai(ch2[last][1],ch2[o][1],mid+1,r);
    } 
}
 
void up(int o)
{
    minn[o]=val[o];
    from[o]=o;
    if(ch[o][0]>0&&minn[ch[o][0]]<minn[o])
    {
    minn[o]=minn[ch[o][0]];
    from[o]=from[ch[o][0]];     
    }
    if(ch[o][1]>0&&minn[ch[o][1]]<minn[o])
    {
    minn[o]=minn[ch[o][1]];
    from[o]=from[ch[o][1]];     
    }
}
void down(int o)
{
    if(rev[o])
    {
        rev[o]^=1;
        rev[ch[o][0]]^=1;
        rev[ch[o][1]]^=1;   
        swap(ch[o][0],ch[o][1]);    
    }
}
void set(int o,int wh,int child)
{
    ch[o][wh]=child;
    fu[child]=o;
    up(o);
}
 int getwh(int o)
 {
    return ch[fu[o]][0]==o?0:1;     
 }
 bool isrt(int o)
 {
    if(fu[o]==0)return 1;
    if(ch[fu[o]][0]==o||ch[fu[o]][1]==o)return 0;
    return 1;
 }
void rotate(int o)
{
    int fa=fu[o];
    int ye=fu[fa];
    int wh=getwh(o);
    bool ysg=0;
    if(isrt(fa)==1)ysg=1;
    set(fa,wh,ch[o][wh^1]);
    set(o,wh^1,fa);
    fu[o]=ye;
    if(ysg==0)
    ch[ye][ch[ye][0]==fa?0:1]=o;    
}
int sta[N];
void splay(int o)
{
    int top=0,i=o;sta[++top]=o;
    for(;isrt(i)==0;i=fu[i])
    {
        sta[++top]=fu[i];
    }
    for(i=top;i>=1;i--)down(sta[i]);
    for(;isrt(o)==0;rotate(o))
    if(isrt(fu[o])==0)
    getwh(o)==getwh(fu[o])?rotate(fu[o]):rotate(o);
}
void access(int o)
{
    int last=0;
    for(;o!=0;last=o,o=fu[o])
    {
        splay(o);
    ch[o][1]=last;  
    up(o);
    }   
}
void huan(int o)
{
    access(o);
    splay(o);
    rev[o]^=1;  
}
void link(int a,int b)
{
    huan(a);
    fu[a]=b;
    access(a);
}
void cut(int a,int b)
{
    huan(a);
    access(b);
    splay(b);
    ch[b][0]=fu[a]=0;
}
int main()
{
//注意初值!!! 
    scanf("%d%d%d%d",&n,&m,&k,&type);
    for(i=1;i<=n;i++)val[i]=9999999;
    for(i=1;i<=m;i++)
    {
    val[i+n]=i;
    scanf("%d%d",&a,&b);
    q[i]=a;z[i]=b;
    if(a!=b)
    {
    access(a);
    splay(a);
    int l1=a,l2=b;
    while(ch[l1][0]!=0)l1=ch[l1][0];
    access(b);
    splay(b);
    while(ch[l2][0]!=0)l2=ch[l2][0];
if(l1==l2)
{
    huan(a);
    access(b);
    splay(b);
    ntr[i]=from[b]-n;
    int l3=from[b];
    cut(q[l3-n],l3);
    cut(l3,z[l3-n]);    
    link(a,i+n);    
    link(b,i+n);
}else
{
    link(a,i+n);    
    link(b,i+n);
}
}else ntr[i]=m;
 
a=ntr[i]+1;b=ntr[i]+1;
rt[i]=++tot;
gai(rt[i-1],rt[i],1,m+1);
}
op=1;
for(i=1;i<=k;i++)
{
    scanf("%d%d",&lll,&rrr);
    if(type==1)lll^=ans,rrr^=ans;
    c=0;a=1,b=lll;
    gai(rt[lll-1],rt[rrr],1,m+1);
    ans=n-c;
printf("%d\n",ans);     
}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值