• UOJ87 mx的仙人掌 虚仙人掌


    UOJ87 mx的仙人掌

    这里没有用传统的方点外接圆点的做法,而是方点虚树上儿子跳到方点所在环上单调队列处理,本质上是一样的.
    注意这是仙人掌的写法,求点双不一样...

    code

    //爽! 
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=900900;
    inline int read(){
    	int x=0,f=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9'){
    		if(ch=='-')f=-1;
    		ch=getchar();
    	}
    	while('0'<=ch&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    	return x*f; 
    }
    int n,m;
    #define pr pair<int,ll>
    #define mk make_pair
    #define fi first
    #define se second
    map<int,int>e[N];
    vector<pr>mp[N];
    void add(int u,int v,int w){//重边(二元环单独处理) 
    	if(e[u].count(v))e[u][v]=min(e[u][v],w);
    	else e[u][v]=w;
    	if(e[v].count(u))e[v][u]=min(e[v][u],w);
    	else e[v][u]=w;
    }
    vector<int>edge[N];
    int tot,dfn[N],low[N],Index,fa[N];
    ll dis[N],sum[N];
    void solve(int x,int rt,int Edge){
    	ll len=dis[x]-dis[rt]+Edge,tmp;
    	++tot;
    	sum[tot]=len;
    	mp[rt].push_back(mk(tot,0));
    	mp[tot].push_back(mk(rt,0));
    	for(int i=x;i!=rt;i=fa[i]){
    		tmp=min(dis[i]-dis[rt],len-dis[i]+dis[rt]);
    		mp[tot].push_back(mk(i,tmp));
    		mp[i].push_back(mk(tot,tmp));
    	}
    }
    void tarjan(int u){
    	dfn[u]=low[u]=++Index;
    	int v;
    	for(map<int,int> :: iterator it=e[u].begin();it!=e[u].end();++it){//递归调用,不然迭代器会出错  
    		v=it->fi;
    		if(v==fa[u])continue;
    		if(!dfn[v]){
    			dis[v]=dis[u]+it->se;
    			fa[v]=u;
    			tarjan(v);
    			low[u]=min(low[u],low[v]);
    			if(dfn[u]<low[v]){
    				mp[u].push_back(mk(v,it->se));
    				mp[v].push_back(mk(u,it->se));
    			}
    			//我现行的圆方树建法会忽略二环 
    		}
    		else low[u]=min(low[u],dfn[v]);
    	}
    	for(map<int,int> :: iterator it=e[u].begin();it!=e[u].end();++it){
    		v=it->fi;
    		if(fa[v]!=u&&dfn[v]>=dfn[u])solve(v,u,it->se);
    	}
    }
    int f[N][20],dep[N],l[N],num;
    ll Len[N];
    void dfs(int u,int fa){
    	f[u][0]=fa,dep[u]=dep[fa]+1;
    	l[u]=++num;
    //	printf("fa=%d son=%d
    ",fa,u);
    //	printf("tree %d %lld
    ",u,Len[u]);
    	for(int v,i=0;i<mp[u].size();++i){
    		ll w;
    		v=mp[u][i].fi;
    		if(v==fa)continue;
    		w=mp[u][i].se;
    		Len[v]=Len[u]+w;
    		dfs(v,u);
    	}
    }
    void prework(){
    	for(int i=1;i<=19;++i){
    		for(int j=1;j<=tot;++j){
    			f[j][i]=f[f[j][i-1]][i-1];
    		}
    	}
    }
    inline int getlca(int x,int y){
    	if(dep[x]<dep[y])swap(x,y);
    	for(int i=19;i>=0;--i){
    		if(dep[f[x][i]]>=dep[y])x=f[x][i];
    		if(dep[x]==dep[y])break;
    	}
    	if(x==y)return x;
    	for(int i=19;i>=0;--i)
    		if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    	return f[x][0];
    }
    int a[N],s[N],top;
    bool cmp(int aa,int bb){
    	return l[aa]<l[bb];
    }
    void build(int x){
    	if(top==0){
    		s[++top]=x;
    		return;
    	}
    	int lca=getlca(s[top],x);
    	while(top>1&&dep[lca]<dep[s[top-1]]){
    		edge[s[top-1]].push_back(s[top]);
    		--top;
    	}
    	while(top>0&&dep[lca]<dep[s[top]]){
    		edge[lca].push_back(s[top]);
    		--top;
    	}
    	if(top==0||s[top]!=lca)s[++top]=lca;
    	s[++top]=x;
    }
    //这里比较刚,没有用传统的方点外接圆点的做法,直接在方点处讨论(因为找不到看得懂的题解了) 
    ll ans,dp[N],g[N],h[N];
    int q[N],c[N];
    //int jump(int pos,int stp){
    //	for(int i=19;i>=0;--i)
    //		if(stp>=(1<<i)){
    //			pos=f[pos][i];
    //			stp-=(1<<i);//学会倍增跳 
    //		}
    //	return pos;
    //}
    void jump(int & u, int k) {
        for(int i = 20 - 1; ~ i; --i) {
            if(k >= (1 << i)) {
                k -= 1 << i;
                u = f[u][i];
            }
        }
    }
    bool cmp2(int aa,int bb){
    	return l[aa]>l[bb];
    }
    void circle(int u,int size){
    //	printf("%d %d
    ",u,size);
    //	printf("cir=%lld
    ",sum[u]);
    
    	sort(c+1,c+size+1,cmp2);
    //	reverse(c+1,c+size+1);
    	//这里两种都可以,但上面一种多个log好理解
    	//下面一种讲究建边细节
    	//具体是我们要从环的底端到顶端,考虑vector存储,那么底端要先加进c,那么底端先访问,即底端先建边,等价于底端先出进虚树的栈,所以底端dfn要大,所以建圆方树时要先建方点到顶端依次到底端的圆点,然后tarjan处理的时候自己注意 
    
    	for(int i=1;i<=size;++i){
    //		printf("%d ",c[i]);
    		h[i]=dis[c[i]];
    		h[i+size]=h[i]+sum[u];//注意用的是原仙人掌上环上的值 
    		g[i]=dp[c[i]];
    		g[i+size]=g[i];
    		//复制一遍 
    //		printf("加油=%lld %lld
    ",h[i],g[i]);
    	}
    //	puts("");
    	int l,r;
    	l=r=1;
    	q[r]=1;
    	size*=2;
    	for(int i=1;i<=size;++i){
    //		printf("加油=%lld %lld
    ",h[i],g[i]);
    	}
    	for(int i=2;i<=size;++i){
    		while(l<=r&&2LL*(h[i]-h[q[l]])>sum[u])++l;//最短路
    		if(l<=r)ans=max(ans,g[i]+g[q[l]]+h[i]-h[q[l]]);
    		while(l<=r&&g[q[r]]-h[q[r]]<=g[i]-h[i])--r;
    		q[++r]=i;
    	}
    //	puts("");
    }
    void tree_dp(int u){
    	dp[u]=0;
    	int size=0;
    	for(int v,i=0;i<edge[u].size();++i){
    		v=edge[u][i];
    		tree_dp(v);
    	}
    	//注意因为要保留栈,所以必须把递归分开写 
    	for(int v,i=0;i<edge[u].size();++i){
    		v=edge[u][i];
    		if(u<=n){
    			ans=max(ans,dp[u]+dp[v]+Len[v]-Len[u]);
    			//树部分 
    		}
    		else{
    			int son=v;
    			jump(son,dep[v]-dep[u]-1);//son要么就是v要么本不存在于虚树上 
    			dp[son]=dp[v]+Len[v]-Len[son];//跳到环上 
    			c[++size]=son; 
    		}
    		dp[u]=max(dp[u],dp[v]+Len[v]-Len[u]);//圆方树上传贡献 
    	}
    	if(u>n)circle(u,size);//环部分
    	edge[u].clear();//清空 
    }
    int main(){
    //	freopen("s.in","r",stdin);
    	n=read(),m=read();
    	tot=n;
    	int u,v,w;
    	for(int i=1;i<=m;++i){
    		u=read(),v=read(),w=read();
    		add(u,v,w),add(v,u,w);
    	}
    	tarjan(1);
    //	puts("dis");
    //	for(int i=1;i<=n;++i)printf("%lld ",dis[i]);
    //	puts("dis");
    	dfs(1,0);
    	prework();
    	int Q=read();
    	while(Q--){
    		int size=read();
    		for(int i=1;i<=size;++i)a[i]=read();
    		sort(a+1,a+size+1,cmp);
    		size=unique(a+1,a+size+1)-a-1;//记得去重(题目要求) 
    		top=0;
    		for(int i=1;i<=size;++i)build(a[i]);
    		while(top>1){
    			edge[s[top-1]].push_back(s[top]);
    			--top;
    		}
    		ans=0;
    		tree_dp(s[top]);//也可以不强制1为根 
    //		for(int i=1;i<=size;++i)printf("%lld ",dp[i]);puts("");
    //		puts("");
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    InstallShield高级应用获取机机所有ORACLE服务列表
    InstallShield高级应用测试ORACL是否可连接
    [分享]Asp.net 页面加载顺序及常用页面事件规律
    InstallShield高级应用检查是否安装ORACLE或SQL Server
    InstallShield高级应用打开文件对话框
    InstallShield高级应用检测系统ServerPack版本,SP2前不支持则 abort
    InstallShield高级应用文件操作
    InstallShield高级应用系列目录
    c# 常用区别总结
    InstallShield高级应用测试Access是否可连接
  • 原文地址:https://www.cnblogs.com/wzhh/p/14380553.html
Copyright © 2020-2023  润新知