zoukankan      html  css  js  c++  java
  • [SDOI2015]寻宝游戏

    题目链接

    题意:有一棵有 (n) 个节点的树,带边权,某些点为关键点,初始没有关键点;有 (q) 个操作,每一次改变一个点的状态(关键点的变为非关键点,反之亦然),每次操作后输出所有关键点形成的极小联通子树的边权和的两倍。

    由于图是一棵树,所以可以认识到从任一个关键点出发,按dfs序从小到大遍历所有其它关键点的路径都是一样的,都是最短距离。建树后跑一遍dfs,之后可以用倍增和lca求任意两点距离;问题就转化为本题的动态操作。

    按照上文的方法,加入或删除一个关键点只要处理它自己、它按dfs序的前驱、后继点的距离即可,故使用一个支持插入,删除,查询前驱、后继的数据结构维护dfs序就可以得出答案了。

    可以使用STL的set存关键点的dfn,但是由于本人set用不好,就直接写了一颗fhq Treap,常数略大……

    代码如下

    #include <cstdio>
    #include <cstdlib>
    #define inf (2100000000)
    typedef long long ll;
    inline ll rd(){
        ll x=0,p=1;
        char a=getchar();
        while((a<48||a>57)&&a!='-')a=getchar();
        if(a=='-')p=-p,a=getchar();
        while(a>47&&a<58){
        	x=(x<<1)+(x<<3)+(a&15);
        	a=getchar();
        }
        return x*p;
    }
    inline void swap(int &x,int &y){int t=x;x=y;y=t;}
    const int N=100002;
    struct Edge{
        int to,next;
        ll w;
    }edge[N<<1];
    int head[N<<1],cnt=0;
    int f[N][22],dep[N],dfn[N],tdfn[N],time,vis[N];
    ll dis[N],ans=0;
    int n,q;
    inline void add(int f,int t,ll d){
        edge[++cnt].next=head[f];
        edge[cnt].to=t;
        edge[cnt].w=d;
        head[f]=cnt;
    }
    inline void dfs(int u,int ft){//dfs求出深度、父亲和距离
        f[u][0]=ft,dep[u]=dep[f[u][0]]+1;
        dfn[u]=++time,tdfn[time]=u;//记录dfn,另开一个数组记录dfn对应的点
        for(int i=head[u];i;i=edge[i].next){
        	int v=edge[i].to;
        	if(v==ft)continue;
        	dis[v]=dis[u]+edge[i].w;
        	dfs(v,u);
        }
    }
    inline int lca(int u,int v){//倍增lca
        if(dep[u]<dep[v])swap(u,v);
        for(int i=18;i>=0;i--)
    	if(dep[f[u][i]]>=dep[v])u=f[u][i];
        if(u==v)return u;
        for(int i=18;i>=0;i--)
    	if(f[u][i]!=f[v][i])
    	    u=f[u][i],v=f[v][i];
        return f[u][0];
    }
    inline ll getdis(int u,int v){return dis[u]+dis[v]-2*dis[lca(u,v)];}
    //以下treap
    int size[N],val[N],son[N][2],rnd[N],Size,root,x,y,z,sz;
    inline void pushup(int rt){size[rt]=size[son[rt][0]]+size[son[rt][1]]+1;}
    inline void split(int rt,int k,int &x,int &y){
        if(!rt){x=y=0;return;}
        else if(val[rt]<=k)x=rt,split(son[rt][1],k,son[rt][1],y);
        else y=rt,split(son[rt][0],k,x,son[rt][0]);
        pushup(rt);
    }
    inline int merge(int a,int b){
        if(!a||!b)return a+b;
        if(rnd[a]<rnd[b])return son[a][1]=merge(son[a][1],b),pushup(a),a;
        else return son[b][0]=merge(a,son[b][0]),pushup(b),b;
    }
    inline int newnode(int k){size[++Size]=1,val[Size]=k,rnd[Size]=random();return Size;}
    inline void insert(int k){
        split(root,k,x,y);
        root=merge(merge(x,newnode(k)),y);
    }
    inline void del(int k){
        split(root,k,x,z),split(x,k-1,x,y);
        y=merge(son[y][0],son[y][1]);
        root=merge(merge(x,y),z);
    }
    inline int kth(int rt,int k){
        while(1){
        	if(k<=size[son[rt][0]])rt=son[rt][0];
        	else if(k==size[son[rt][0]]+1)return rt;
        	else k-=size[son[rt][0]]+1,rt=son[rt][1];
        }
        return -1;
    }
    inline int pre(int k){
        split(root,k-1,x,y);
        int ans=val[kth(x,size[x])];
        root=merge(x,y);
        return ans;
    }
    inline int suc(int k){
        split(root,k,x,y);
        int ans=val[kth(y,1)];
        root=merge(x,y);
        return ans;
    }
    int main(){
        n=rd(),q=rd();
        for(int i=1;i<n;i++){
        	int u=rd(),v=rd();ll w=rd();
        	add(u,v,w),add(v,u,w);
        }
        dfs(1,0);
        for(int j=1;j<=18;j++)
        	for(int i=1;i<=n;i++)
    	        f[i][j]=f[f[i][j-1]][j-1];
        insert(inf),insert(-inf),sz=2;//先插入inf防爆,记录真实的数据个数
        while(q--){
        	int u=rd();
        	if(!vis[u])insert(dfn[u]),sz++;//若原先没有这个点,先插入再计算
        	int p=pre(dfn[u]),s=suc(dfn[u]);
        	if(p==-inf)p=val[kth(root,sz-1)];//如果超过,找最(前)后的点
        	if(s== inf)s=val[kth(root,2)];
        	if(vis[u])del(dfn[u]),sz--;//若原先有这个点,先计算再删除
        	ll d=getdis(u,tdfn[p])+getdis(u,tdfn[s])-getdis(tdfn[p],tdfn[s]);//关于贡献的计算,画出一棵树推一下即可得到一个点的贡献即可得到式子
        	if(vis[u])vis[u]=0,ans-=d;
        	else vis[u]=1,ans+=d;//更新答案
        	printf("%lld
    ",ans);
        }
        return 0;
    }
    
  • 相关阅读:
    微软工具连接
    [转贴]生成缩略图
    突破验证,安装Media Player11.
    【转贴】Sourcecode and Code Snippets
    AppArch(一):User Interface
    【转】中国的OA要走的路还很长
    对WebService的在企业应用中的思考。
    [转贴]按文件类型获取其图标
    信息系统分析方法
    【转】WebService第一次调用正常,第二次调用超时的解决办法。
  • 原文地址:https://www.cnblogs.com/wsk1202/p/11702721.html
Copyright © 2011-2022 走看看