zoukankan      html  css  js  c++  java
  • HDU contest808 ACM多校第7场 Problem

    首先嘚瑟一下这场比赛的排名:59

    (第一次看到这么多 emmmm)

    好了进入正文QAQ

    ...这道题啊,思路很清晰啊。

    首先你看到树上路径边权和,然后还带修改,不是显然可以想到 树剖+线段树 维护重链么?

    然后你再看啊,这是一个连通图,然后有 n 个点 n 条边,于是很显然会有一个环(然后就构成了一个 仙人掌 ...不过我并不了解仙人掌)

    然后你再看!这里只会有一个环,我们假设没有这个环,那么这就是一道 树剖 模板题,那么我们可不可以特殊地,让这个环当根,除这个环以外的其他节点来简单 树剖 呢?

    恩,这不是显然么? 于是我们考虑怎么处理那个简单环...emmmm!考虑暴力维护,考虑暴力维护,那么我们是 O(n) 的,而修改是 O(1) 的。

    那么我们处理一下前缀和呢?很遗憾,这样只不过是将两个复杂度反了一下。那么我们考虑用数据结构优化(中和)这个复杂度。

    没错,就是树状数组维护前缀和,达到查询和修改都是 O(log n) 的复杂度(并且常数很小),于是这道题成功的包含了 树剖、线段树、树状数组 这三个算法。

    咳咳,别着急啊,这不是还没说怎么找环啊。

    emmm...相信不用我说,你就一秒想到了 tarjan 。 对啊,显然啊。

    然后就有四个算法了。

    还有吗?(难道还不够?四个算法除了树状数组好大其他码量大的很啊)

    emmm...没了,真没了

    你真要说有的话,就是标记环时候要用的深搜了...(其实完全可以 tarjan 的时候把环标记出来的好伐)

    然后,然后...上代码(错误示范,但思想正确,现在起码能过样例了)

    //by Judge
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #define ls k<<1
    #define rs k<<1|1
    #define mid (l+r>>1)
    #define ll long long
    using namespace std;
    const int M=1e5+111;
    inline int read(){
    	int x=0,f=1; char c=getchar();
    	for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    	for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f;
    }
    int n,m,pat,cnt,tim,stop;
    ll t[M<<2],ff[M];
    int id[M],s[M],in[M],rk[M],pos[M];
    int head[M],dfn[M],low[M],stk[M],blg[M];
    int siz[M],dep[M],f[M],son[M],top[M],val[M];
    struct Edge{
    	int to,val,next,frm;
    	Edge(int to,int val,int next,int frm): to(to),val(val),next(next),frm(frm){} Edge(){}
    }e[M<<2];
    inline void add(int u,int v,int c,int w){
    	e[++pat]=Edge(v,c,head[u],w),head[u]=pat;
    	e[++pat]=Edge(u,c,head[v],w),head[v]=pat;
    }
    #define v e[i].to
    #define cc e[i].val
    /*            tarjan缩点 处理环          */
    void tarjan(int u,int fa){
    	dfn[u]=low[u]=++tim,stk[++stop]=u;
    	for(int i=head[u];i;i=e[i].next) if(v!=fa){
    		if(!dfn[v]) tarjan(v,u),low[u]=min(low[u],low[v]);
    		else low[u]=min(low[u],dfn[v]);
    	}
    	if(dfn[u]==low[u]){
    		int j=stk[stop];
    		if(j==u) {--stop,blg[u]=u; return ; }
    		do{ j=stk[stop--],blg[id[pos[j]=++cnt]=in[j]=j]=u; }while(j!=u);
    		for(j=1;j<=cnt;++j) for(int i=head[id[j]];i;i=e[i].next)
    			if(blg[v]!=u) add(u,v,cc,e[i].frm),in[v]=id[j];
    			else if(pos[v]%cnt+1==j) s[e[i].frm]=id[j],val[id[j]]=cc;
    	}
    }
    /*                树剖建树               */
    void dfs1(int u){
    	siz[u]=1, dep[u]=dep[f[u]]+1;
    	for(int i=head[u];i;i=e[i].next){
    		if(blg[v]!=v || v==f[u] || f[v]) continue;
    		in[u]!=u?in[v]=in[u]:0,f[v]=u,val[v]=cc,dfs1(v);
    		s[e[i].frm]=v,siz[u]+=siz[v],siz[v]>siz[son[u]]?son[u]=v:0;
    	}
    }
    void dfs2(int u){
    	if(!top[u]) top[u]=u; dfn[u]=++tim,rk[tim]=u;
    	if(!son[u]) return; top[son[u]]=top[u],dfs2(son[u]);
    	for(int i=head[u];i;i=e[i].next)
    		if(blg[v]==v && v!=f[u] && v!=son[u]) dfs2(v);
    }
    #undef v
    #undef cc
    /*        binary-indexed-tree             */
    inline int lowbit(int x){
    	return x&(-x);
    }
    inline void add(int x,int y){
    	for(;x<=cnt;x+=lowbit(x)) ff[x]+=y;
    }
    inline ll get_pre(int x,int y,int z=cnt){
    	ll r1=0,r2=0; if(x>y) swap(x,y);
    	for(;y;y-=lowbit(y)) r1+=ff[y];
    	for(;x;x-=lowbit(x)) r1-=ff[x];
    	for(;z;z-=lowbit(z)) r2+=ff[z];
    	return min(r1,r2-r1);
    }
    inline void cbuild(){
    	for(int i=1;i<=cnt;++i) ff[i]=0;
    	for(int i=1;i<=cnt;++i) add(i,val[id[i]]);
    }
    inline void change(int x,int y){
    	add(x,-val[id[x]]),val[id[x]]=y,add(x,val[id[x]]);
    }
    /*          segment-tree                 */
    void build(int k,int l,int r){
    	if(l==r) return (void)(t[k]=val[rk[l]]);
    	build(ls,l,mid), build(rs,mid+1,r), t[k]=t[ls]+t[rs];
    }
    void update(int k,int l,int r,int x,int y){
    	if(l>x || r<x) return ; if(l==r) return (void)(t[k]=y);
    	update(ls,l,mid,x,y),update(rs,mid+1,r,x,y),t[k]=t[ls]+t[rs];
    }
    ll query(int k,int l,int r,int L,int R){
    	if(L>r || l>R) return 0; if(L<=l && r<=R) return t[k];
    	return query(ls,l,mid,L,R)+query(rs,mid+1,r,L,R); 
    }
    /*            树剖询问                 */
    inline ll get_sum(int u,int v){
    	ll ans=0; int inu=in[u],inv=in[v]; u=blg[u],v=blg[v];
    //	printf("%d  %d
    ",u,v); printf("%lld
    ",query(1,1,tim,1,tim));
    	while(top[u]^top[v]){
    		if(dep[top[u]]<dep[top[v]]) swap(u,v);
    		ans+=query(1,1,tim,dfn[top[u]],dfn[u]),u=f[top[u]];
    	} if(u^v){
    		if(dep[u]>dep[v]) swap(u,v);
    		ans+=query(1,1,tim,dfn[u]+1,dfn[v]);
    	} if(u==id[cnt]) ans+=get_pre(pos[inu],pos[inv]);
    	return ans;
    }
    int main(){
    //	freopen("testdata.in","r",stdin); 
    	int T=read(),x,y,opt;
    	while(T--){
    		n=read(),m=read(),pat=cnt=tim=0;
    		for(int i=1;i<=n;++i)
    			f[i]=in[i]=head[i]=top[i]=dfn[i]=son[i]=0;
    		for(int i=1,u,v,c;i<=n;++i)
    			u=read(),v=read(),
    			c=read(),add(u,v,c,i);
    		tarjan(1,0),cbuild(),tim=0;
    		dfs1(id[cnt]),dfs2(id[cnt]),build(1,1,tim);
    		while(m--){
    			opt=read(),x=read(),y=read();
    			if(opt) printf("%lld
    ",get_sum(x,y));
    			else if(blg[s[x]]==id[cnt]) change(pos[s[x]],y);
    			else update(1,1,tim,dfn[s[x]],y);
    		}
    	}return 0;
    }
    

    然后这是某个大佬的代码QAQ(居然比我的好写那么多!!!只有 LCA 差分 树状数组 和 RMQ!)

    思路貌似是先不管 第 n 条边,用前 n-1 条边 ,然后...这个鬼畜的 欧拉序列+ RMQ 求祖先你就将就着看下吧(能树剖干嘛打这玩意儿?又不整树上莫队那玩意儿?)

    但是这个代码显然是错误的,因为题目并没有保证第 n 条边就是在环上的啊。(但解决此问题只需要我们深搜找环或者tarjan找话即可)

    但是这个想法真的妙!(你想想看这比我的思路简洁多少!) 多出来那条边用来求更优答案就好了还整什么仙人掌?

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    #define maxn 100005
    typedef long long LL;
    struct Edge { int to,next,id; }edge[maxn<<1];
    int n,a[maxn],head[maxn],dep[maxn<<1],cnt,pos[maxn],dfs_seq[maxn<<1],dfn,f[maxn<<1][20];
    int L[maxn],R[maxn],dfs_clock,G[maxn];
    LL W[maxn],C[maxn];
    inline void add(int u,int v,int id) {
    	edge[cnt].to=v;
    	edge[cnt].next=head[u];
    	edge[cnt].id=id;
    	head[u]=cnt++;
    }
    inline int lowbit(int x) {
    	return (x)&(-x);
    }
    void init() {
    	memset(head,-1,sizeof(head));
    	memset(pos,-1,sizeof(pos));
    	memset(C,0,sizeof(C));
    	cnt=dfn=0;
    	dfs_clock=0;
    }
    void dfs(int u,int deep) {
    	dfs_seq[dfn]=u,dep[dfn]=deep,pos[u]=dfn++;
    	L[u]=++dfs_clock;
    	for(int i=head[u]; ~i; i=edge[i].next) {
    		int v=edge[i].to;
    		if(pos[v]==-1) {
    			G[edge[i].id]=v;                //important
    			dfs(v,deep+1);
    			dfs_seq[dfn]=u,dep[dfn++]=deep;
    		}
    	}
    	R[u]=dfs_clock;
    }
    void init_RMQ(int n) {
    	for(int i=1; i<=n; ++i) f[i][0]=i;
    	for(int j=1; (1<<j)<=n; ++j)
    		for(int i=1; i+(1<<j)-1<=n; ++i) {
    			if(dep[f[i][j-1]]<dep[f[i+(1<<(j-1))][j-1]]) f[i][j]=f[i][j-1];
    			else f[i][j]=f[i+(1<<(j-1))][j-1];
    		}
    }
    inline int RMQ(int L,int R) {
    	int k=0;
    	while(1<<(k+1)<=R-L+1) ++k;
    	if(dep[f[L][k]]<dep[f[R-(1<<k)+1][k]]) return f[L][k];
    	return f[R-(1<<k)+1][k];
    }
    inline int lca(int u,int v) {
    	if(pos[u]>pos[v]) return dfs_seq[RMQ(pos[v],pos[u])];
    	return dfs_seq[RMQ(pos[u],pos[v])];
    }
    inline void update(int i,LL x) {
    	for(; i<=n; i+=lowbit(i)) C[i]+=x;
    }
    inline LL sum(int i) {
    	LL s=0;
    	for(; i>0; i-=lowbit(i)) s+=C[i];
    	return s;
    }
    inline LL dist(int u,int v) {
    	return sum(L[u])+sum(L[v])-2*sum(L[lca(u,v)]);
    }
    int main() {
    	int i,u,v,k,q,s,T; LL w;
    	scanf("%d",&T);
    	while(T--) {
    		scanf("%d%d",&n,&q),init();
    		for(i=1; i<=n-1; ++i) {
    			scanf("%d%d%lld",&u,&v,&w);
    			add(u,v,i);
    			add(v,u,i);
    			W[i]=w;
    		}
    		dfs(1,0),init_RMQ(dfn-1);
    		int X,Y; LL Z;
    		scanf("%d%d%lld",&X,&Y,&Z),W[n] = Z;;               //第n条边
    		for(i=1; i<n; ++i) {
    			update(L[G[i]],W[i]);
    			update(R[G[i]]+1,-W[i]);
    		}
    		while(q--) {
    			scanf("%d",&k);
    			if(k==0) {
    				scanf("%d%lld",&u,&w);
    				if(u==n)
    					W[n] = w;
    				else {
    					update(L[G[u]],w-W[u]);
    					update(R[G[u]]+1,-w+W[u]);
    					W[u]=w;
    				}
    			} else {
    				scanf("%d%d",&u,&v);
    				LL ans=dist(u,v);  //常规情况
    				ans=min(ans,dist(u,X)+dist(v,Y)+Z);  //第二种
    				ans=min(ans,dist(u,Y)+dist(v,X)+Z);  //第三种
                                    //就是说这里多了两种经过多出来那条边的情况 啊(整片代码最大的价值,其他都能用树剖艹)
    				printf("%lld
    ",ans);
    			}
    		}
    	}
    	return 0;
    }
    

    顺便推荐一道我刷了 n 多树剖模板题还 debug 了半个小时的题目: 月下“毛景树”,BZOJ 权限题,但洛谷上也有.

    这题就是树剖求路径上的边权和,然后带修改(区间修改!两个区间修改!),代码如下,然后有点地方加了解释,其他都不难理解,模板:

    //by Judge
    #include<iostream>
    #include<cstdio>
    #define ls k<<1
    #define rs k<<1|1
    #define mid (l+r>>1)
    using namespace std;
    const int M=1e5+111;
    const int inf=1e9+7;
    inline int read(){
    	int x=0,f=1; char c=getchar();
    	for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    	for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    	return x*f;
    }
    inline char cread(){
    	char c=getchar(); while(!islower(c)) c=getchar(); return c;
    }
    int n,pat,tim;
    int head[M],id[M],s[M],t[M<<2],tag[M<<2],ad[M<<2];
    int siz[M],dep[M],f[M],son[M],dfn[M],top[M],val[M];
    struct Edge{
    	int to,val,next;
    }e[M<<1];
    inline void add(int u,int v,int c){
    	e[++pat]=(Edge){ v,c,head[u] },head[u]=pat;
    	e[++pat]=(Edge){ u,c,head[v] },head[v]=pat;
    }
    #define v e[i].to
    void dfs1(int u){
    	siz[u]=1;
    	for(int i=head[u];i;i=e[i].next){
    		if(v==f[u]) continue;
    		f[v]=u,dep[v]=dep[u]+1,val[v]=e[i].val;
    		dfs1(v),siz[u]+=siz[v],s[i+1>>1]=v;
    		if(siz[v]>siz[son[u]]) son[u]=v; 
    	}
    }
    void dfs2(int u){
    	dfn[u]=++tim,id[tim]=u;
    	if(!top[u]) top[u]=u; if(!son[u]) return ;
    	top[son[u]]=top[u],dfs2(son[u]);
    	for(int i=head[u];i;i=e[i].next)
    		if(v!=f[u] && v!=son[u]) dfs2(v);
    }
    #undef v
    inline void build(int k,int l,int r){
    	if(l==r) return (void)(t[k]=val[id[l]]);
    	build(ls,l,mid),build(rs,mid+1,r),t[k]=max(t[ls],t[rs]);
    }
    inline void pushdown(int k){   //pushdown 的时候我们考虑两个懒标记在任何时候都只会留下一个,于是判断两下就好了 
    	if(tag[k]) t[ls]=t[rs]=tag[ls]=tag[rs]=tag[k],tag[k]=ad[ls]=ad[rs]=0;
    	else if(ad[k]){
    		if(tag[ls]) t[ls]=tag[ls]+=ad[k]; else ad[ls]+=ad[k],t[ls]+=ad[k];
    		if(tag[rs]) t[rs]=tag[rs]+=ad[k]; else ad[rs]+=ad[k],t[rs]+=ad[k];
    		ad[k]=0;
    	}
    }
    void update_to(int k,int l,int r,int L,int R,int x){
    	if(l>R || L>r) return ; if(L<=l && r<=R) return (void)(tag[k]=t[k]=x,ad[k]=0);  //修改的话,add标记直接作废 
    	pushdown(k), update_to(ls,l,mid,L,R,x),update_to(rs,mid+1,r,L,R,x),t[k]=max(t[ls],t[rs]);
    }
    void update_add(int k,int l,int r,int L,int R,int x){
    	if(l>R || L>r) return ; if(L<=l && r<=R){ if(tag[k]) t[k]=tag[k]+=x; else ad[k]+=x,t[k]+=x; return ;}  //区间加的话,根据是否有修改标记来做 
    	pushdown(k), update_add(ls,l,mid,L,R,x),update_add(rs,mid+1,r,L,R,x),t[k]=max(t[ls],t[rs]);
    }
    int query(int k,int l,int r,int L,int R){
    	if(l>R || L>r) return -inf; if(L<=l && r<=R) return t[k];
    	pushdown(k); return max(query(ls,l,mid,L,R),query(rs,mid+1,r,L,R));
    }
    inline void Change(){
    	int x=dfn[s[read()]],w=read(); update_to(1,1,tim,x,x,w);
    }
    inline void Cover(){
    	int u=read(),v=read(),w=read();
    	while(top[u]!=top[v]){
    		if(dep[top[u]]<dep[top[v]]) swap(u,v);
    		update_to(1,1,tim,dfn[top[u]],dfn[u],w),u=f[top[u]];
    	} if(u==v) return ; if(dep[u]>dep[v]) swap(u,v);
    	update_to(1,1,tim,dfn[u]+1,dfn[v],w);
    }
    inline void Add(){
    	int u=read(),v=read(),w=read();
    	while(top[u]!=top[v]){
    		if(dep[top[u]]<dep[top[v]]) swap(u,v);
    		update_add(1,1,tim,dfn[top[u]],dfn[u],w),u=f[top[u]];
    	} if(u==v) return ; if(dep[u]>dep[v]) swap(u,v);
    	update_add(1,1,tim,dfn[u]+1,dfn[v],w);
    }
    inline void Max(){
    	int u=read(),v=read(),res=-inf;
    	while(top[u]^top[v]){
    		if(dep[top[u]]<dep[top[v]]) swap(u,v);
    		res=max(res,query(1,1,tim,dfn[top[u]],dfn[u])),u=f[top[u]];
    	}
    	if(u!=v){
    		if(dep[u]>dep[v]) swap(u,v);
    		res=max(res,query(1,1,tim,dfn[u]+1,dfn[v]));
    	} printf("%d
    ",res);
    }
    int main(){
    	n=read(); int u,v,w,opt;
    	for(int i=1;i<n;++i)
    		u=read(),v=read(),w=read(),add(u,v,w);
    	dep[1]=1,dfs1(1),dfs2(1),build(1,1,tim);
    	while((opt=cread())!='t')
    		switch(opt){
    			case 'h': Change(); break;
    			case 'o': Cover(); break;
    			case 'd': Add(); break;
    			case 'a': Max(); break;
    		} return 0;
    }
    

    刷完这两道题...路径边权和什么的(以及一些恶心的线段树双重懒标记题)都不是问题了吧(理论上)

  • 相关阅读:
    如何使用SAP Intelligent Robotic Process Automation自动操作Excel
    OpenSAML 使用引导 IV: 安全特性
    Spring Cloud Zuul 网关使用与 OAuth2.0 认证授权服务
    微服务架构集大成者—Spring Cloud (转载)
    Spring Cloud Eureka 服务注册列表显示 IP 配置问题
    使用 Notification API 开启浏览器桌面提醒
    SignalR 中使用 MessagePack 序列化提高 WebSocket 通信性能
    配置 Nginx 的目录浏览功能
    关于 Nginx 配置 WebSocket 400 问题
    Migrate from ASP.NET Core 2.0 to 2.1
  • 原文地址:https://www.cnblogs.com/Judge/p/9470522.html
Copyright © 2011-2022 走看看