【BZOJ3514】Codechef MARCH14 GERALD07加强版,LCT+主席树

Time:2016.08.06
Author:xiaoyimi
转载注明出处谢谢


传送门
思路:
今天模拟题T3
结果是看都没看??
n,m<=1000
每次直接暴力并查集,O(n)判断
复杂度 O(qm)
考虑离线
应该是询问分块排序,类似莫队然后上LCT或者可持久化并查集(口胡get√)
满分做法
考虑LCT这个东西
按照边加入的时间戳维护最小生成树
加入边i(x,y)会形成环时
求出(x,y)所在联通块中时间戳最小的边j并删去(就是最早加入的边)
记录a[i]=j
(注意:如果i为自环,就把a赋值为+∞)
每次处理[l,r]间的询问时
查询[l,r]内,求得a[i]<l的数量
很显然这是主席树可以做的事
连离散化都不用的
注意:无
代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define M 200005
#define ls(x) tr[x].ch[0]
#define rs(x) tr[x].ch[1]
using namespace std;
int in()
{
    int t=0;char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') t=(t<<3)+(t<<1)+ch-48,ch=getchar();
    return t;
}
int n=in(),m=in(),k=in(),lastans,cnt;
bool tp=in(),lazy[M<<1];
int root[M],mi[M<<1],fa[M<<1],son[M<<1][2],siz[M<<1],val[M<<1],stacks[M<<1],a[M];
struct Chairman_tree{
    int siz,ch[2];
}tr[M*19];
bool isroot(int x){return son[fa[x]][0]!=x&&son[fa[x]][1]!=x;}
void ct(int x)
{
    mi[x]=x;
    if (val[mi[x]]>val[mi[son[x][0]]])
        mi[x]=mi[son[x][0]];
    if (val[mi[x]]>val[mi[son[x][1]]])
        mi[x]=mi[son[x][1]];
    siz[x]=siz[son[x][0]]+siz[son[x][1]]+1;
}
void pushdown(int x)
{
    if (!lazy[x]) return;
    swap(son[x][0],son[x][1]);
    lazy[son[x][0]]^=1;
    lazy[son[x][1]]^=1;
    lazy[x]=0;
}
void rorate(int x,bool f)
{
    int y=fa[x];
    fa[son[x][f]]=y;
    son[y][!f]=son[x][f];
    fa[x]=fa[y];
    if (!isroot(y))
    {
        if (son[fa[y]][0]==y) son[fa[y]][0]=x;
        else son[fa[y]][1]=x;
    }
    fa[y]=x;
    son[x][f]=y;
    ct(y);ct(x);
}
void splay(int x)
{
    int y,cnt=0,k=x;
    stacks[++cnt]=k;
    while (!isroot(k)) stacks[++cnt]=k=fa[k];
    for (;cnt;cnt--) pushdown(stacks[cnt]);
    for (y=fa[x];!isroot(x);y=fa[x])
    {
        if (isroot(y))
            rorate(x,son[y][0]==x);
        else if (son[fa[y]][0]==y)
        {
            if (son[y][0]==x) rorate(y,1);
            else rorate(x,0);
            rorate(x,1);
        }
        else
        {
            if (son[y][1]==x) rorate(y,0);
            else rorate(x,1);
            rorate(x,0);
        }
    }
}
void access(int x)
{
    for (int y=0;x;y=x,x=fa[x])
        splay(x),
        son[x][1]=y,
        ct(x);
}
void link(int x,int y)
{
    access(x);
    splay(x);
    lazy[x]^=1;
    fa[x]=y;
}
void cut(int x,int y)
{
    access(x);
    splay(x);
    lazy[x]^=1;
    access(y);
    splay(y);
    fa[x]=son[y][0]=0;
    ct(y);
}
int findroot(int x)
{
    access(x);
    splay(x);
    while (son[x][0]) x=son[x][0];
    return x;
}
int ask(int x,int y)
{
    access(x);
    splay(x);
    lazy[x]^=1;
    access(y);
    splay(y);
    return mi[y];
}
void build(int rt,int L,int R,int now,int v)
{
    tr[now].siz=tr[rt].siz+1;
    if (L==R) return;
    int mid=L+R>>1;
    if (mid>=v)
        rs(now)=rs(rt),
        ls(now)=++cnt,
        build(ls(rt),L,mid,ls(now),v);
    else
        ls(now)=ls(rt),
        rs(now)=++cnt,
        build(rs(rt),mid+1,R,rs(now),v); 
}
int get(int L,int R,int begin,int end,int v)
{
    if (begin==end) return tr[R].siz-tr[L].siz;
    int mid=begin+end>>1;
    if (v>mid)
        return get(rs(L),rs(R),mid+1,end,v)+tr[ls(R)].siz-tr[ls(L)].siz;
    else
        return get(ls(L),ls(R),begin,mid,v);
}
main()
{
    for (int i=0;i<=n;i++)   val[i]=1<<30;
    for (int i=n+1;i<=n+m;i++)  val[i]=i-n;
    int x,y,p;
    for (int i=1;i<=m;i++)
    {
        x=in();y=in();
        if (x==y) {a[i]=m;continue;}
        if (findroot(x)==findroot(y))
            p=ask(x,y),
            a[i]=p-n,
            cut(x,p),
            cut(y,p);
        link(x,n+i);link(y,n+i);
    }
    for (int i=0;i<=m;i++) root[i]=++cnt;
    for (int i=1;i<=m;i++)
        build(root[i-1],0,m,root[i],a[i]);
    for (;k;k--)
    {
        x=in();y=in();
        if (tp) x^=lastans,y^=lastans;
        printf("%d\n",lastans=n-get(root[x-1],root[y],0,m,x-1));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值