zoukankan      html  css  js  c++  java
  • WC2013-糖果公园

    给出一棵树,每个点有颜色,每种颜色有一个(v)属性,第(i)次经过颜色(j)可以获得(w_i*v_j)的价值。多次修改单点颜色,查询路径的价值。(n,m,qle 10^5)

    分析

    可以发现,经过颜色的顺序是没有关系的,只需要知道有多少个颜色就可以了。也就是说,这个问题可以快速转移,修改是单点修改,加上可以离线,使用莫队算法。

    算是重新学了一次莫队。现有的莫队算法有两种,线上普通莫队和线上带修改莫队。树上莫队只不过是把线上的情况用欧拉序的方法变成了线上的(用于链上询问)或者用dfs序变成线上的(用于子树询问)。下面讨论普通莫队和带修改莫队。

    一般莫队算法

    对于离线区间无修改询问,莫队算法是一种很优秀的处理方式。莫队算法的思想是通过询问排序和快速转移解决询问。一般地,如果对于区间([l,r])的询问可以转移到([l-1,r],[l,r-1],[l+1,r],[l,r+1]),那么这个问题就可以用莫队处理。

    我们把序列分块,设块大小为(B),块数为(S),显然有(B*S=n)。把所有询问按照左端点所在块为第一关键字,右端点位置为第二关键字排序,按这样的顺序处理每个询问。初始时(l,r)都在0的位置。

    对于处理到的当前询问,我们直接暴力把l和r移动到相应的位置。设序列长度为(n),询问数为(m),分析复杂度:

    • 对于左端点
      • 左端点在块内的移动每次最多为(B),最多移动(m)次,所以复杂度为(O(mB))
      • 左端点在块之间移动最多发生(S)次,每次最多跳(B)个位置,所以复杂度为(O(BS)=O(n))
    • 对于右端点
      • 左端点在一个块中的时候,右端点是从小到大的,所以最多移动(m)次,左端点有(S)个可以在的块,所以复杂度为(O(Sm))
      • 左端点在块之间移动的时候,右端点最多移动(n)个位置,一共会发生(nS)次,复杂度为(O(nS))

    这样分析,设一次转移的复杂度为(O(k)),那么总复杂度为(O(k(mB+n+Sm+nS))),当(B=sqrt frac{n(m+n)}{m})的时候取到最小值。当(n,m)同阶的时候,复杂度取到(O(n^frac{3}{2}k))

    带修改莫队算法

    莫队带上了修改,那么每次询问其实变成了三维,((l,r,t)),分别为询问的左右端点和这是在第几次修改后的询问。

    做法如下,左右端点都按照所在块编号排序,时间从小到大排序,这样处理询问。分析三维的移动:

    • 左右端点的移动同普通莫队左端点的移动分析,复杂度都为(O(mB+n))
    • 时间的移动,由于是按照左右端点所在块排序后从小到大排序,所以当左右端点的块是确定的时候,时间的移动是最多(O(m))的,而左右端点所在块最多为(O(S^2))种,所以复杂度为(O(mS^2))

    (B)取到(n^frac{2}{3})时,复杂度达到(O(n^frac{5}{3}k))

    这样来看,这道题就是一个很简单的带修改莫队啦~

    代码

    跑的比本地快啊,只用了16s。有一个需要注意的地方,当(n^frac{2}{3}<1)的时候,要取个max,这样就不会出现除以0的问题了。

    #include<cstdio>
    #include<cstring>
    #include<cctype>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    typedef long long giant;
    const int theb=1e7;
    char buf[theb],*pt=buf;
    int read() {
    	int x=0,f=1;
    	for (;!isdigit(*pt);++pt) if (*pt=='-') f=-1;
    	for (;isdigit(*pt);++pt) x=x*10+*pt-'0';
    	return x*f;
    }
    void write(giant x) {
    	if (!x) puts("0"); else {
    		static char s[25];
    		int tot=0;
    		while (x) s[++tot]=x%10+'0',x/=10;
    		while (tot) putchar(s[tot--]);
    		puts("");
    	}
    }
    const int maxn=2e5+10;
    const int maxj=17;
    giant theans=0,ans[maxn];
    int blos,n,m,q,col[maxn],cs[maxn],block[maxn],dep[maxn];
    giant val[maxn],w[maxn];
    int aux[maxn],first[maxn],second[maxn],dfn[maxn],dft=0,current;
    bool ina[maxn];
    struct C {
    	int p,ord,nw;
    } change[maxn];
    struct Q {
    	int u,v,l,r,t,id;
    	inline bool operator < (const Q &a) const {
    		if (block[l]!=block[a.l]) return block[l]<block[a.l];
    		if (block[r]!=block[a.l]) return block[r]<block[a.r];
    		return t<a.t;
    	}
    } qs[maxn];
    struct graph {
    	struct edge {
    		int v,nxt;
    	} e[maxn<<1];
    	int h[maxn],tot,f[maxn][maxj];
    	graph ():tot(0) {}
    	void add(int u,int v) {
    		e[++tot]=(edge){v,h[u]};
    		h[u]=tot;
    	}
    	void dfs(int x,int fa) {
    		f[x][0]=fa;
    		dep[x]=dep[fa]+1;
    		for (register int j=1;j<maxj;++j) f[x][j]=f[f[x][j-1]][j-1];
    		first[x]=++dft;
    		dfn[dft]=x;
    		for (register int i=h[x],v=e[i].v;i;i=e[i].nxt,v=e[i].v) if (v!=fa) dfs(v,x);
    		second[x]=++dft;
    		dfn[dft]=x;
    	}
    	int lca(int x,int y) {
    		if (dep[x]<dep[y]) swap(x,y);
    		for (register int j=maxj-1;j>=0;--j) if (dep[f[x][j]]>=dep[y]) x=f[x][j];
    		if (x==y) return x;
    		for (register int j=maxj-1;j>=0;--j) if (f[x][j]!=f[y][j]) x=f[x][j],y=f[y][j];
    		return f[x][0];
    	}
    } G;
    inline void flow(int x) {
    	C &c=change[x];
    	if (ina[c.p]) theans-=w[cs[col[c.p]]--]*val[col[c.p]],theans+=w[++cs[col[c.p]=c.nw]]*val[col[c.p]]; 
    	else col[c.p]=c.nw;
    }
    inline void back(int x) {
    	C &c=change[x];
    	if (ina[c.p]) theans-=w[cs[col[c.p]]--]*val[col[c.p]],theans+=w[++cs[col[c.p]=c.ord]]*val[col[c.p]];
    	else col[c.p]=c.ord;
    }
    inline void in(int x) {
    	theans+=w[++cs[col[x]]]*val[col[x]];
    }
    inline void out(int x) {
    	theans-=w[cs[col[x]]--]*val[col[x]];
    } 
    inline void deal(int x) {
    	x=dfn[x];
    	ina[x]?out(x):in(x);
    	ina[x]^=true;
    }
    inline giant add(int x) {
    	return w[cs[col[x]]+1]*val[col[x]];
    }
    int main() {
    #ifndef ONLINE_JUDGE
    	freopen("test.in","r",stdin);
    	freopen("my.out","w",stdout);
    #endif
    	fread(buf,1,theb,stdin);
    	n=read(),m=read(),q=read();
    	blos=max((int)pow(n*2,2.0/3.0)/3,1);
    	for (register int i=1;i<=(n<<1);++i) block[i]=(i-1)/blos+1;
    	for (register int i=1;i<=m;++i) val[i]=read();
    	for (register int i=1;i<=n;++i) w[i]=read();
    	for (register int i=1;i<n;++i) {
    		int u=read(),v=read();
    		G.add(u,v),G.add(v,u);
    	}
    	for (register int i=1;i<=n;++i) col[i]=aux[i]=read();
    	G.dfs(1,1);
    	int tm=0,all=0;
    	for (register int i=1;i<=q;++i) {
    		int op=read(),u=read(),v=read();
    		if (op) {
    			if (dep[u]>dep[v]) swap(u,v);
    			int lca=G.lca(u,v);
    			if (lca!=u && first[u]>first[v]) swap(u,v); 
    			int l,r;
    			if (u==lca) l=first[u],r=first[v]; else l=second[u],r=first[v];
    			++all;
    			qs[all]=(Q){u,v,l,r,tm,all};
    		} else {
    			change[++tm]=(C){u,aux[u],v};
    			aux[u]=v;
    		}
    	}
    	sort(qs+1,qs+all+1);
    	int nl=1,nr=0,nt=0;
    	for (register int i=1;i<=all;++i) {
    		current=i;
    		int u=qs[i].u,v=qs[i].v,t=qs[i].t;
    		int lca=G.lca(u,v);
    		int l=qs[i].l,r=qs[i].r;
    		while (nt<t) flow(++nt);
    		while (nt>t) back(nt--);
    		while (nl>l) deal(--nl);
    		while (nr<r) deal(++nr);
    		while (nl<l) deal(nl++);
    		while (nr>r) deal(nr--);
    		ans[qs[i].id]=theans+(!ina[lca]?add(lca):0);
    	}
    	for (register int i=1;i<=all;++i) write(ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    jqGrid api 中文说明
    jsp + js + 前端弹出框
    js中关于带数字类型参数传参丢失首位数字0问题
    java中WGS84坐标(ios)转换BD-09坐标(百度坐标)
    Java中的“浅复制”与“深复制”
    Git错误:error:failed to push some refs to 'git@gitee.com:name/project.git'
    git操作教程
    线程调度及进程调度
    同步锁Lock
    多线程案例
  • 原文地址:https://www.cnblogs.com/owenyu/p/6820362.html
Copyright © 2011-2022 走看看