zoukankan      html  css  js  c++  java
  • Gym102341I Infernape

    Link
    我们可以先统计任意(k-1)个关键点覆盖的连通块大小之和,再减去(k-1)乘上(k)个关键点都覆盖的连通块大小。
    所有(k-1)个关键点覆盖的连通块可以先预处理关键点覆盖范围的前缀交和后缀交,再枚举未被选的关键点(i)计算(pre_{i-1})(suf_{i+1})的交。
    不难发现任意多个关键点覆盖的连通块求交集之后仍然可以表示为(U(u,k))的形式。
    这个(u)可能在某条边上,因此我们拆边为点。然后点分治统计即可。

    #include<cmath>
    #include<cctype>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<algorithm>
    using i64=long long;
    const int N=300007,inf=1e9;
    namespace In
    {
        char ibuf[1<<23|1],*iS=ibuf;
        void init(){fread(ibuf,1,1<<23,stdin);}
        int read(){int x=0;while(isspace(*iS))++iS;while(isdigit(*iS))(x*=10)+=*iS++&15;return x;}
    }using namespace In;
    namespace Tree
    {
        int n,fa[N][18],dep[N];std::vector<int>e[N];
        void add(int u,int v){e[u].push_back(v),e[v].push_back(u);}
        void dfs(int u)
        {
    	for(int i=1;i<18;++i) fa[u][i]=fa[fa[u][i-1]][i-1];
    	for(int v:e[u]) if(v^fa[u][0]) fa[v][0]=u,dep[v]=dep[u]+1,dfs(v);
        }
        int jump(int u,int d)
        {
    	for(int i=17;~i;--i) if(d>>i&1) u=fa[u][i];
    	return u;
        }
        int lca(int u,int v)
        {
    	if(dep[u]<dep[v]) std::swap(u,v);
    	if((u=jump(u,dep[u]-dep[v]))==v) return u;
    	for(int i=17;~i;--i) if(fa[u][i]^fa[v][i]) u=fa[u][i],v=fa[v][i];
    	return fa[u][0];
        }
    }using namespace Tree;
    namespace Divide
    {
        int q,root,mn,deg,vis[N],size[N],cnt[N];i64 ans[N];
        struct node{int u,k;}a[N],pre[N],suf[N];
        struct query{int k,id,x;};std::vector<query>qry[N];
        node operator+(const node&a,const node&b)
        {
    	int p=lca(a.u,b.u),len=dep[a.u]+dep[b.u]-2*dep[p],k;
    	if(a.k<0||b.k<0||a.k+b.k<len) return {-1,-1};
    	if(a.k>=len+b.k) return b;
    	if(b.k>=len+a.k) return a;
    	return k=std::min(a.k,b.k)-(len-abs(a.k-b.k))/2,node{dep[a.u]-dep[p]>=a.k-k? jump(a.u,a.k-k):jump(b.u,b.k-k),k};
        }
        void find(int u,int fa,int s)
        {
    	int mx=0;size[u]=1;
    	for(int v:e[u]) if(!vis[v]&&v^fa) find(v,u,s),size[u]+=size[v],mx=std::max(mx,size[v]);
    	if((mx=std::max(mx,s-size[u]))<mn) root=u,mn=mx;
        }
        void update(int k)
        {
    	cnt[0]*=k;
    	for(int i=1;i<=deg;++i) cnt[i]=(cnt[i]+cnt[i-1])*k;
        }
        void dfs1(int u,int fa,int d,int k)
        {
    	cnt[d]+=u<=n? k:0,deg=std::max(deg,d);
    	for(int v:e[u]) if(!vis[v]&&v^fa) dfs1(v,u,d+1,k);
        }
        void dfs2(int u,int fa,int d)
        {
    	for(auto[k,id,x]:qry[u]) if(k>=d) ans[id]+=1ll*x*cnt[std::min(deg,k-d)];
    	for(int v:e[u]) if(!vis[v]&&v^fa) dfs2(v,u,d+1);
        }
        void divide(int u,int s)
        {
    	mn=s,find(u,0,s),vis[u=root]=1,find(u,0,s);
    	deg=0,dfs1(u,0,0,1),update(1);
    	dfs2(u,0,0),memset(cnt,0,4*deg+4);
    	for(int v:e[u]) if(!vis[v]) deg=0,dfs1(v,u,1,-1),update(1),dfs2(v,u,1),memset(cnt,0,4*deg+4);
    	for(int v:e[u]) if(!vis[v]) divide(v,size[v]);
        }
    }using namespace Divide;
    int main()
    {
        init(),n=read();
        for(int i=1;i<n;++i) add(read(),i+n),add(read(),i+n);
        dfs(dep[1]=1),q=read();
        for(int i=1;i<=q;++i)
        {
    	int k=read();node now;pre[0]=suf[k+1]={1,inf};
    	for(int j=1;j<=k;++j) a[j]={read(),read()*2};
    	for(int j=1;j<=k;++j) pre[j]=pre[j-1]+a[j];
    	for(int j=k;j;--j) suf[j]=suf[j+1]+a[j];
    	if(~suf[1].u) qry[suf[1].u].push_back({suf[1].k,i,1-k});
    	for(int j=1;j<=k;++j) if(now=pre[j-1]+suf[j+1],~now.u) qry[now.u].push_back({now.k,i,1});
        }
        divide(1,n);
        for(int i=1;i<=q;++i) printf("%lld
    ",ans[i]);
    }
    
  • 相关阅读:
    多级弹出菜单jQuery插件ZoneMenu
    jquery投色子动画
    推荐一个内容滚动jquery插件
    足球宝贝来了,任你旋转(CSS3)
    jQuery切换网页皮肤保存到Cookie实例
    程序员用HTML5给女朋友制作的3D相册
    jquery模拟LCD 时钟
    jQuery刮彩票兑奖效果
    滚动变色的文字js特效
    jQuery实现右上角点击后滑下来的竖向菜单
  • 原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12913005.html
Copyright © 2011-2022 走看看