zoukankan      html  css  js  c++  java
  • [BZOJ3460] Jc的宿舍

    bzoj
    题面放一下

    Description

    WC2014后无数人来膜拜jc,但是来膜拜的人实在太多了, 而且很多人是一连膜拜好几天。所以jc给这些人建了一座树 形的宿舍,而根节点(1号节点)住着jc。然而,由于设计 的原因,宿舍中只有一个水龙头。于是晚上打水就成了问题。 所有人都有一个大小不同的水桶,第i个结点住着的人的水 桶灌满要Ti的时间。水龙头一开始在jc的宿舍,但是水龙 头的位置会发生变化。当一个人去打水,他走的一定是到水 龙头的最短距离,而且他路过的所有宿舍中住的人都会和他 一起去打水。现在有n个人入住,发生了m个事件。
    1.C i 表示水龙头在第i个宿舍
    2.Q i 表示住在i宿舍的人出发去打水。
    对于每个Q i,你需要告诉jc这次去打水的所有人最少的 总等待时间。

    Input

    第一行三个整数n,m,key,接下来一行n个整数表示Ti(小于等于10^7),接下来一行n个数表示每个节点的父亲,保证根节点的父亲为0。接下来m行每行表述一个事件。对于每个Q操作,若输入为Q k,则实际的(k=(k+(pre \% 2)*key) \% n+1)(pre)为上一个询问的答案,若是第一个 询问则(pre=0)。一开始水龙头在1号节点。

    sol

    每一次的询问就是查询树上从(k)点到水龙头位置的路径信息。
    想写树上莫队。发现强制在线?
    仔细想一下发现这个强制在线是假的,因为一来修改操作没有加密,二来(pre)只会导致询问有两种不同可能。只要对这两种分别求解在输出的时候判一下就好了。

    莫队维护的东西是所有人的总等待时间。按照贪心的思路,显然是打水时长小的人排在前面。
    我们现在考虑新加入一个打水时长为(time)的人。那么这个人就要想办法插到打水队伍里面。因为打水时间相同的人顺序任意,我们假设他插到了与他打水时间相同的所有人中的最后一个位置。
    那么这时候,排在他前面的人的等待时间不会变。他自己的等待时间为(time+)所有排在他前面,打水时间小于等于(time)的时长之和。而排在他后面的人,每个人的打水时长都增加了(time)
    所以就能轻松地写出每次加入/删除一个点后答案的变化量了。
    把打水时间离散化后相当于前缀和、后缀和的形式,直接树状数组一波。

    code

    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    #define ll long long
    int gi()
    {
        int x=0,w=1;char ch=getchar();
        while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
        if (ch=='-') w=0,ch=getchar();
        while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
        return w?x:-x;
    }
    const int N = 1e5+5;
    int n,block,m,key,val[N],o[N],len,to[N<<1],nxt[N<<1],head[N],cnt;
    int fa[N],dep[N],sz[N],son[N],top[N],dfn[N];
    int ccnt,bl[N],s[N],tp,lst=1,vis[N];
    ll c1[N],c2[N],Ans,ans[N],pre;
    struct query{
    	int u,v,id;
    	bool operator < (const query &b) const
    		{
    			if (bl[u]!=bl[b.u]) return bl[u]<bl[b.u];
    			return bl[v]<bl[b.v];
    		}
    }q[N];
    void link(int u,int v){to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;}
    void dfs1(int u,int f)
    {
    	fa[u]=f;dep[u]=dep[f]+1;sz[u]=1;
    	for (int e=head[u];e;e=nxt[e])
    	{
    		int v=to[e];if (v==f) continue;
    		dfs1(v,u);
    		sz[u]+=sz[v];if (sz[v]>sz[son[u]]) son[u]=v;
    	}
    }
    void dfs2(int u,int up)
    {
    	top[u]=up;dfn[++cnt]=u;int ttp=tp;
    	if (son[u]) dfs2(son[u],up);
    	if (tp-ttp>=block) {++ccnt;while (tp>ttp) bl[s[tp--]]=ccnt;}
    	for (int e=head[u];e;e=nxt[e])
    	{
    		int v=to[e];if (v==fa[u]||v==son[u]) continue;
    		dfs2(v,v);
    		if (tp-ttp>=block) {++ccnt;while (tp>ttp) bl[s[tp--]]=ccnt;}
    	}
    	s[++tp]=u;
    }
    int getlca(int u,int v)
    {
    	while (top[u]^top[v])
    	{
    		if (dep[top[u]]<dep[top[v]]) swap(u,v);
    		u=fa[top[u]];
    	}
    	return dep[u]<dep[v]?u:v;
    }
    void Add(int pos,int val)
    {
    	for (int k=pos;k<=len;k+=k&-k)
    		c1[k]+=val*o[pos],c2[k]+=val;
    }
    ll Sum(int k){ll res=0;while(k)res+=c1[k],k^=k&-k;return res;}
    ll Size(int k){ll res=0;while(k)res+=c2[k],k^=k&-k;return res;}
    void update(int x)
    {
    	if (!vis[x])
    	{
    		Ans+=o[val[x]]+Sum(val[x])+(ll)o[val[x]]*(Size(len)-Size(val[x]));
    		Add(val[x],1);vis[x]=1;
    	}
    	else
    	{
    		Add(val[x],-1);vis[x]=0;
    		Ans-=o[val[x]]+Sum(val[x])+(ll)o[val[x]]*(Size(len)-Size(val[x]));
    	}
    }
    void change(int u,int v)
    {
    	while (u^v)
    		if (dep[u]>dep[v])  update(u),u=fa[u];
    		else update(v),v=fa[v];
    }
    int main()
    {
    	n=gi();block=pow(n,0.6);m=gi();key=gi();
    	for (int i=1;i<=n;++i) val[i]=o[i]=gi();
    	sort(o+1,o+n+1);len=unique(o+1,o+n+1)-o-1;
    	for (int i=1;i<=n;++i) val[i]=lower_bound(o+1,o+len+1,val[i])-o;
    	for (int i=1;i<=n;++i)
    	{
    		int f=gi();
    		if (f) link(f,i),link(i,f);
    	}
    	dfs1(1,0);cnt=0;dfs2(1,1);cnt=0;
    	if (tp) {++ccnt;while (tp) bl[s[tp--]]=ccnt;}
    	for (int i=1;i<=m;++i)
    	{
    		char ch=getchar();
    		while (ch!='C'&&ch!='Q') ch=getchar();
    		if (ch=='C') lst=gi();
    		else{
    			int k=gi();
    			q[++cnt]=(query){lst,k%n+1,cnt};
    			if (dfn[q[i].u]>dfn[q[i].v]) swap(q[i].u,q[i].v);
    			q[++cnt]=(query){lst,(k+key)%n+1,cnt};
    			if (dfn[q[i].u]>dfn[q[i].v]) swap(q[i].u,q[i].v);
    		}
    	}
    	sort(q+1,q+cnt+1);
    	change(q[1].u,q[1].v);
    	int gg=getlca(q[1].u,q[1].v);
    	update(gg);ans[q[1].id]=Ans;update(gg);
    	for (int i=2;i<=cnt;++i)
    	{
    		change(q[i].u,q[i-1].u);
    		change(q[i].v,q[i-1].v);
    		gg=getlca(q[i].u,q[i].v);
    		update(gg);ans[q[i].id]=Ans;update(gg);
    	}
    	for (int i=1;(i<<1)<=cnt;++i)
    		if (pre&1) printf("%lld
    ",pre=ans[i<<1]);
    		else printf("%lld
    ",pre=ans[(i<<1)-1]);
    	return 0;
    }
    
  • 相关阅读:
    Linux内核网络协议栈优化总纲
    Java实现 蓝桥杯VIP 算法训练 连续正整数的和
    Java实现 蓝桥杯VIP 算法训练 连续正整数的和
    Java实现 蓝桥杯VIP 算法训练 寂寞的数
    Java实现 蓝桥杯VIP 算法训练 寂寞的数
    Java实现 蓝桥杯VIP 算法训练 学做菜
    Java实现 蓝桥杯VIP 算法训练 学做菜
    Java实现 蓝桥杯VIP 算法训练 判断字符位置
    Java实现 蓝桥杯VIP 算法训练 判断字符位置
    Java实现 蓝桥杯VIP 算法训练 链表数据求和操作
  • 原文地址:https://www.cnblogs.com/zhoushuyu/p/8474686.html
Copyright © 2011-2022 走看看