zoukankan      html  css  js  c++  java
  • 【BZOJ】3052: [wc2013]糖果公园

    http://www.lydsy.com/JudgeOnline/problem.php?id=3052

    题意:n个带颜色的点(m种),q次询问,每次询问x到y的路径上sum{w[次数]*v[颜色]},可以单点修改颜色。(n, m, q<=100000)

    #include <bits/stdc++.h>
    using namespace std;
    const int N=100005, M=100005;
    typedef long long ll;
    inline int getint() { int x=0; char c=getchar(); while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') (x*=10)+=c-'0', c=getchar(); return x; }
    inline void print(ll a) { if(!a) return; print(a/10); putchar('0'+(a%10));}
    int ihead[N], cnt, blo[N<<1], f[N][17], FF[N], LL[N], tot, dep[N], cal[N], pos[N<<1], col[M], n, m, W[N], V[M], qu, n_ask, n_tm, col_pre[N];
    ll Ans[M], ans;
    bool st[N];
    struct E { int next, to; }e[N<<1];
    struct Q { int x, y, lca, id, tm; }q[M];
    struct T { int x, y, last; }Time[M];
    inline bool cmp(const Q &a, const Q &b) { return blo[a.x]==blo[b.x]?(blo[a.y]==blo[b.y]?a.tm<b.tm:blo[a.y]<blo[b.y]):blo[a.x]<blo[b.x]; }
    inline void add(int x, int y) { e[++cnt]=(E){ihead[x], y}; ihead[x]=cnt; e[++cnt]=(E){ihead[y], x}; ihead[y]=cnt; }
    void dfs(int x) {
    	pos[FF[x]=++tot]=x;
    	for(int i=1; i<=16; ++i) f[x][i]=f[f[x][i-1]][i-1];
    	for(int i=ihead[x]; i; i=e[i].next) if(e[i].to!=f[x][0])
    		dep[e[i].to]=dep[x]+1, f[e[i].to][0]=x, dfs(e[i].to);
    	pos[LL[x]=++tot]=x;
    }
    inline int LCA(int x, int y) {
    	if(dep[x]<dep[y]) swap(x, y);
    	int d=dep[x]-dep[y];
    	for(int i=16; i>=0; --i) if((d>>i)&1) x=f[x][i]; if(x==y) return x;
    	for(int i=16; i>=0; --i) if(f[x][i]!=f[y][i]) x=f[x][i], y=f[y][i];
    	return f[x][0];
    }
    inline void update(int x) {
    	if(st[x]) { ans-=(ll)V[col[x]]*W[cal[col[x]]]; --cal[col[x]]; }
    	else { ++cal[col[x]]; ans+=(ll)V[col[x]]*W[cal[col[x]]];  }
    	st[x]=!st[x];
    }
    inline void change(int a, int b) {
    	if(st[a]) { update(a); col[a]=b; update(a); }
    	else col[a]=b;
    }
    inline void timechange(int &now, int goal) {
    	while(now<goal) ++now, change(Time[now].x, Time[now].y);
    	while(now>goal) change(Time[now].x, Time[now].last), --now;
    }
    void work() {
    	int l=1, r=0, now=0, nl, nr;
    	sort(q+1, q+1+n_ask, cmp);
    	for(int i=1; i<=n_ask; ++i) {
    		nl=q[i].x; nr=q[i].y;
    		timechange(now, q[i].tm);
    		while(l<nl) update(pos[l++]);
    		while(l>nl) update(pos[--l]);
    		while(r<nr) update(pos[++r]);
    		while(r>nr) update(pos[r--]);
    		if(q[i].lca) update(q[i].lca);
    		Ans[q[i].id]=ans;
    		if(q[i].lca) update(q[i].lca);
    	}
    	while(r>=l) update(pos[r--]);
    }
    void pre() {
    	dfs((n+1)>>1);
    	int nn=n<<1, sq=pow(nn, 2.0/3)*0.5;
    	for(int i=1; i<=nn; ++i) blo[i]=(i-1)/sq;
    	for(int i=1; i<=qu; ++i) {
    		int type=getint(), x=getint(), y=getint();
    		if(!type) {
    			++n_tm;
    			Time[n_tm]=(T){x, y, col_pre[x]}; col_pre[x]=y;
    			continue;
    		}
    		++n_ask;
    		if(FF[x]>FF[y]) swap(x, y);
    		int lca=LCA(x, y);
    		if(lca==x) 	q[n_ask]=(Q){FF[x], FF[y], 0, n_ask, n_tm};
    		else 		q[n_ask]=(Q){LL[x], FF[y], lca, n_ask, n_tm};
    	}
    }
    int main() {
    	n=getint(); m=getint(); qu=getint();
    	for(int i=1; i<=m; ++i) V[i]=getint();
    	for(int i=1; i<=n; ++i) W[i]=getint();
    	for(int i=1; i<n; ++i) {
    		int x=getint(), y=getint();
    		add(x, y);
    	}
    	for(int i=1; i<=n; ++i) col[i]=col_pre[i]=getint();
    	pre();
    	work();
    	for(int i=1; i<=n_ask; ++i) print(Ans[i]), puts("");
    	return 0;
    }
    

      

    一开始我直接在每个修改之间计算答案= =然后果断T了= =QAQ

    膜拜vfk.....

    首先分块是三元分块!并且要进行修改的操作以及逆操作(最坏变成O(n)辣= =)

    第三元就是询问的时间。

    然后写完后发现还是好慢QAQ

    因为我把块大小就是分成了O(n^0.5)QAQ

    继续膜拜vfk

    发现要分块成O(n^(2/3)).....则有O(n^(1/3))个块...然后具体证明请看 http://vfleaking.blog.163.com/blog/static/174807634201311011201627/

    艾雨青大神犇教导我们,将树分块!
    如前所述的分块方法。当时艾雨青神犇讲题的时候的分块方法没听清 T_T,上面的分块方法是我自己YY出来的。
    取B = n ^ (2 / 3),设 nBlo为块的个数,用bloNum[v]来代表v所在块的编号。(block number)
    则同一个块内任意两结点的距离为O(n ^ (2 / 3))的。
    按照之前我说的方式对询问进行排序,按顺序作答。
    注意到(bloNum[curV], bloNum[curU])一共有nBlo ^ 2个取值。
    那么如果移动一次,curV还在原来的块,curU还在原来的块,这种移动的总时间复杂度是O(nBlo ^ 2 * q)的。(因为curTi还要移动)
    如果移动一次,curV不在原来的块,curU不在原来的块,这种移动发生的次数最多为 nBlo ^ 2。因为我是排好序的了嘛,相同块的是放在一起的。而这种移动发生一次最坏是O(n + n + q) = O(n)。(n、q是同阶的)
    所以这样回答所有询问,时间复杂度就是O(nBlo ^ 2 * n)的。
    由于B = n ^ (2 / 3),块的大小介于[B, 3 * B]之间。
    则nBlo = O(n ^ (1 / 3))
    则时间复杂度为O(n ^ (5 / 3))。

    (如果不会dfs序的话请看我上一篇博文【BZOJ】3757: 苹果树

  • 相关阅读:
    linux的别名(alias/unalias)
    asp.net <%%> <%#%><%=%><%@%><%$%>用法与区别
    SQL获取刚插入的记录的自动增长列ID的值
    包和继承
    面对对象编程(封装)
    面对对象编程(上)
    数组(下)
    java数组-如何在一堆数据中使用数组!
    Request和Response学习笔记5
    Request和Response学习笔记4
  • 原文地址:https://www.cnblogs.com/iwtwiioi/p/4335639.html
Copyright © 2011-2022 走看看