zoukankan      html  css  js  c++  java
  • [WC2014] 紫荆花之恋

    链接_

    终于过了。。。

    首先这题显然只能一个个处理,即每加入一个点计算贡献该点与其他点的贡献。

    考虑转换题意:假设我们找到一个 (p)(u ightarrow v) 路径上,那么 (dis(u,v)=dis(u,p)+dis(p,v))

    那么就有 (dis(u,p)+dis(v,p)leq d_u+d_v),移项得 (d_u-dis(u,p)geq dis(v,p)-d_v)

    我们假设 (u) 是我们当前的加入点,那么我们令 (w_{p,v}=dis(v,p)-d_v),最后我们只需要对于所有 (p) 求其中 (leq d_u-dis(u,p)) 的个数即可。这个就是一个平衡树维护的东西。

    直接计算显然是 (O(n^2log n))。但按照套路,考虑点分树,可以把需要计算的 (p) 缩小到 (O(log n)) 个。而且根据点分治的性质,(u)(v) 的路径上有且仅有一个点在点分树上同时为 (u,v) 的祖先,所以可以计算。

    但是我们还有一个前提是 (p)(u ightarrow v) 路径上。如果直接按上述方式计算,上层分治中心也会计算这个值。按照点分治的套路,考虑容斥,即对于当前待插入的点 (u),我们在它的每个点分祖先 (p) 上都再维护一个平衡树表示容斥的标记,然后当处理到 (p) 时,我们减去这个标记,表示 (u) 已经处理过了,抵消上层分治中心的处理 (u) 的贡献。

    这样就处理完了点分树的部分,时间复杂度 (O(nlog^2n))。但是当你兴奋地打完代码后,才发现这题最毒瘤的地方:强制在线。(毒瘤题居然不给离线一分)。

    对于这类强制在线题目有一个比较暴力的通解,就是根号重构。但是这里会退化成 (O(nsqrt nlog^2 n)) 显然爆炸。

    但是我们考虑,虽然动态加点可能会导致整颗点分树全部发生变化,但是我们并不需要它一定是点分树,我们只需要它的树深是 (O(log n)) 的就好了。这个在平衡树中有一个很经典的数据结构:替罪羊树。

    具体来说,我们定义一个常数 (alpha),只有其中一颗子树的大小超过整棵树的 (n imesalpha),我们才对这棵树进行重建。

    所以插入一个点时,我们对它在点分树上的祖先找到第一个不平衡的树,然后对这棵子树重新构造点分树。

    在替罪羊树中 (alpha=0.7) 左右比较优秀,但是这里要稍微大一些,因为一次重构的代价是 (O(s_ulog^2 s_u)) 的。

    具体复杂度我也不会证,但好像均摊下来不超过 (O(nlog^3 n)) ,稍微卡卡常就能过。

    还有这里如果用splay/非旋treap会被卡常。。。

    当然写有旋treap就没这么多事了。

    然后由于这题有很多次重构,总重构复杂度会到 (O(nlog^3 n)),但是实际用的点只有 (O(nlog^2 n)),所以要垃圾回收。

    最后由于要重构很多次,rand()好像有点慢。。。不如直接模仿替罪羊树的写法,不平衡就旋(怎么感觉在哪里看到过这种操作),也不知道为什么这样就会变快?

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #define N 100010
    #define M N*40
    using namespace std;
    const double alp=0.85;
    int nxt[N<<1],to[N<<1],w[N<<1],head[N],cnt;
    void add(int u,int v,int w1)
    {
    	nxt[++cnt]=head[u];
    	to[cnt]=v;
    	w[cnt]=w1;
    	head[u]=cnt;
    }
    int val[M],rnd[M],ch[M][2],siz[M],tot;	
    int sta[M],totp;
    void update(int u){siz[u]=siz[ch[u][0]]+siz[ch[u][1]]+1;}
    int new_node(int v)
    {
    	int u=totp?sta[totp--]:++tot;
    //	if(tot>M-10) throw;
    //	rnd[u]=rand();
    	val[u]=v;siz[u]=1;ch[u][0]=ch[u][1]=0;
    	return u;
    }
    void rotate(int &u,int d)
    {
    	int v=ch[u][d];
    //	if(rnd[u]<rnd[v]) return;
    	if(siz[u]*0.75>siz[v]) return;
    	ch[u][d]=ch[v][!d],ch[v][!d]=u;
    	update(u);update(v);u=v;
    }
    void insert(int &u,int v)
    {
    	if(!u){u=new_node(v);return;}
    	siz[u]++;
    	if(v<=val[u]) insert(ch[u][0],v),rotate(u,0);
    	else insert(ch[u][1],v),rotate(u,1);
    }
    void clear(int &u){if(!u) return;clear(ch[u][0]),clear(ch[u][1]);sta[++totp]=u;u=siz[u]=rnd[u]=0;}
    int find(int u,int v)
    {
    	if(!u) return 0;
    	if(val[u]<=v) return find(ch[u][1],v)+siz[ch[u][0]]+1;
    	return find(ch[u][0],v);
    }
    int r1[N],r2[N],asiz[N];
    int vis[N],ut;
    int usiz,root,msiz;
    void dfs1(int u,int f)
    {
    	clear(r1[u]),clear(r2[u]);asiz[u]=1;
    	for(int i=head[u];i;i=nxt[i])
    	{
    		int v=to[i];
    		if(vis[v]==ut || v==f) continue;
    		dfs1(v,u);
    		asiz[u]+=asiz[v];
    	}
    }
    void dfs2(int u,int f)
    {
    	int res=usiz-asiz[u];
    	for(int i=head[u];i;i=nxt[i])
    	{
    		int v=to[i];
    		if(vis[v]==ut || v==f) continue;
    		dfs2(v,u);
    		res=max(res,asiz[v]);
    	}
    	if(res<msiz) msiz=res,root=u;
    }
    int qu[N],qd[N],qtot;
    void dfs3(int u,int f,int d)
    {
    	qu[++qtot]=u,qd[qtot]=d;
    	for(int i=head[u];i;i=nxt[i])
    	{
    		int v=to[i];
    		if(vis[v]==ut || v==f) continue;
    		dfs3(v,u,d+w[i]);
    	}
    }
    int dep[N],c[N];
    #define D 110
    int f[N][D],fd[N][D];
    
    void rebuild(int u,int d)
    {
    	dfs1(u,0);
    	usiz=msiz=asiz[u];root=u;
    	dfs2(u,0);
    	qtot=0;
    	dep[u=root]=d;
    	vis[u]=ut;
    	dfs3(u,0,0);
    	for(int i=1;i<=qtot;i++)
    	{
    		int v=qu[i];
    		f[v][d]=u,fd[v][d]=qd[i];
    		insert(r1[u],fd[v][d]-c[v]);
    		if(d) insert(r2[u],fd[v][d-1]-c[v]);
    	}
    	for(int i=head[u];i;i=nxt[i])
    	{
    		int v=to[i];
    		if(vis[v]!=ut) rebuild(v,d+1);
    	}
    }
    int main()
    {
    	int n;
    	scanf("%*d%d%*d%*d%d",&n,&c[1]);
    	f[1][0]=1,insert(r1[1],-c[1]);
    	puts("0");
    	long long ans=0;
    	for(int i=2;i<=n;i++)
    	{
    		int fa,w;
    		scanf("%d%d%d",&fa,&w,&c[i]);fa^=ans%1000000000;
    		add(fa,i,w),add(i,fa,w);
    		f[i][dep[i]=dep[fa]+1]=i,fd[i][dep[i]]=0;
    		if(dep[i]>D-10) throw;
    		for(int j=0;j<dep[i];j++)
    		{
    			f[i][j]=f[fa][j];
    			fd[i][j]=fd[fa][j]+w;
    			ans+=find(r1[f[i][j]],c[i]-fd[i][j]);
    			if(j) ans-=find(r2[f[i][j]],c[i]-fd[i][j-1]);
    		}
    		for(int j=0;j<=dep[i];j++)
    		{
    			insert(r1[f[i][j]],fd[i][j]-c[i]);
    			if(j) insert(r2[f[i][j]],fd[i][j-1]-c[i]);
    		}
    		++ut;
    		for(int j=0;j<dep[i];vis[f[i][j]]=ut,j++)
    		if(siz[r1[f[i][j]]]*alp<siz[r1[f[i][j+1]]]){rebuild(f[i][j],j);break;}
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Java 面向对象(二)封装
    Java 面向对象(一)面向对象思想
    Java 字符串(二)字符串常用操作
    Java 字符串(一)字符串初始化
    JavaScript 流程控制(二)循环结构
    【剑指Offer-知识迁移能力】面试题58:翻转单词顺序
    【剑指Offer-知识迁移能力】面试题57.2:和为s的连续整数序列
    【剑指Offer-知识迁移能力】面试题57:合为s的两个数字
    【剑指Offer-知识迁移能力】面试题56:数组中只出现一次的两个数字
    【剑指Offer-知识迁移能力】面试题55.2:平衡二叉树
  • 原文地址:https://www.cnblogs.com/Flying2018/p/13645687.html
Copyright © 2011-2022 走看看