2020牛客多校第九场H Distance(树分块+二次离线莫队)

本文介绍了使用树分块和二次离线莫队算法解决HDistance问题的思路,虽然实现了12s的运行时间,但作者认为通过改进分块方法可以进一步提高效率。
摘要由CSDN通过智能技术生成

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6874

思路:思路其实就是题解那个,已经讲得很清楚了,就是有些细节要写好点,详细的可以看代码,我跑了12s,应该是分块方法不够优秀,如果能分成严格的\sqrt{n}应该能快不少

#pragma GCC optimize(2)
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <bitset>
#include <cmath>
#include <cctype>
#include <iostream>
#include <unordered_map>
#include <algorithm>
#include <string>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <sstream>
#include <iomanip>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll inff = 0x3f3f3f3f3f3f3f3f;
#define FOR(i,a,b) for(int i(a);i<=(b);++i)
#define FOL(i,a,b) for(int i(a);i>=(b);--i)
#define REW(a,b) memset(a,b,sizeof(a))
#define inf int(0x3f3f3f3f)
#define si(a) scanf("%d",&a)
#define sl(a) scanf("%lld",&a)
#define sd(a) scanf("%lf",&a)
#define ss(a) scanf("%s",a)
#define pb push_back
#define lc (d<<1)
#define Pll pair<ll,ll>
#define P pair<int,int>
#define pi acos(-1)
const int N=2e5+8;
int n,m,b[N],tot,top[1008],sz,k[N],key[N],head[N];
int num,fa[N],block[N],x,y,z,vis[N],faa[1008],dd;
unsigned w[N],val[1008],val1[1008],sd[N],val2[N],tem[1008],ds[N],pre[N];
struct as{
int l,r,d;
unsigned ss;}d[N];
struct node{
int next,c,to;}e[N<<1];
void add(int a,int b,int c)
{
    e[tot].next=head[a];
    e[tot].to=b;
    e[tot].c=c;
    head[a]=tot++;
}
vector<tuple<int,int,int>>gg[N];
bool cmp(as x,as y){
    if(b[x.l]!=b[y.l])return b[x.l]<b[y.l];
    if(b[x.l]&1)return x.r<y.r;
    return x.r>y.r;
}
bool cmp1(as a,as b){return a.d<b.d;}
inline int read()
{
    int X=0,w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
int dfs(int u)//这种分块方式保证了两个关键点的lca也是关键点,这对这题还是比较重要的
{
    int si=1,ma=0;//ma记录他有几个儿子的子树有关键点
    for(int i=head[u];i!=-1;i=e[i].next)
    {
        int v=e[i].to;
        if(v==fa[u]) continue;
        ds[v]=e[i].c;w[v]=w[u]+ds[v];
        fa[v]=u,si+=dfs(v);
        if(k[v]) ma++;//k==1表示v子树中存在关键点
    }
    if(si>=sz||ma>1||u==1) k[u]=key[u]=1;//如果它本身是关键点 或 有2个及以上的儿子的子树有关键点,则它是关键点
    else if(ma) k[u]=1;//它的子树有关键点
    return key[u]?0:si;
}//这样分块不能严格保证块的大小,因为可能很多个子树的大小都接近sz,
//但由于每次修改都只会进一颗子树,所以关系不大
void dfs1(int u)
{
    if(key[u]) block[u]=++num,top[num]=u;
    for(int i=head[u];i!=-1;i=e[i].next)
    {
        int v=e[i].to;
        if(v==fa[u]) continue;
        dfs1(v);
    }
    int v=u;
    if(key[u])
    {
        while(!key[fa[v]]&&fa[v]) v=fa[v],block[v]=block[u];//将路径上的点加入当前块
        v=fa[v];faa[block[u]]=block[v];//构建虚树中的边
    }
    else if(!block[u])
    {
        while(!key[fa[v]]&&fa[v]) v=fa[v];
        v=fa[v],block[u]=-block[v];//不在路径上的点属于它最近关键点所在块,(分开保存,所以存成负数)
        //在这题其实两关键点之间的点不应该存负数,所以下面dfs的时候改了
    }
}
void dfs1(int u,unsigned z,int rt,int fg)
{
    if(z)
    {
        if(fg) val2[u]+=z;
        else sd[u]=z,block[u]=dd;
    }
    else if(u!=rt) return;
    for(int i=head[u];i!=-1;i=e[i].next)
    {
        int v=e[i].to;
        if(v==fa[u]||key[v]) continue;
        dfs1(v,vis[v]?z+ds[v]:z,rt,fg);
    }
}
int id(int u){return (block[u]<0)?-block[u]:faa[block[u]];}
void add(int u,int fg)
{
    int x,y=id(u);
    x=u;y=top[y];
    while(x!=y&&fa[x]) vis[x]=1,x=fa[x];
    dfs1(y,0,y,fg);
    x=u;y=top[id(u)];
    while(x!=y&&fa[x]) vis[x]=0,x=fa[x];
}
void add(int u)
{
    int x=block[u],y=id(u);
    FOR(i,0,num) tem[i]=0;
    while(faa[y]) tem[y]=w[top[y]]-w[top[faa[y]]],val1[y]++,y=faa[y];
    if(block[u]>0) tem[x]=sd[u];
    if(key[u]) val1[x]++,tem[x]=w[top[x]]-w[top[faa[x]]];//这地方注意点,找bug找了半天
    FOR(i,1,num) val[i]+=tem[i]+=tem[faa[i]];
    if(!key[u]) add(u,1);
}
unsigned gans(int u)
{
    int x=block[u],y=(x<0)?-x:faa[x];
    return key[u]?val[x]:val[y]+val1[abs(x)]*sd[u]+val2[u];
}
int main()
{
    cin.tie(0);
    cout.tie(0);
    int t;
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        int zz=sqrt(n);
        FOR(i,1,num) val[i]=val1[i]=0;
        w[n+1]=num=tot=0,sz=zz/2;
        FOR(i,0,n+1) b[i]=i/zz+1,head[i]=-1,sd[i]=k[i]=key[i]=0;
        FOR(i,0,n+1) gg[i].clear(),block[i]=val2[i]=0;
        FOR(i,1,n-1)
        {
            x=read(),y=read(),z=read();
            add(x,y,z),add(y,x,z);
        }
        dfs(1),dfs1(1);
        FOR(i,1,num) dd=i,add(top[i],0);
        FOR(i,1,m) d[i].l=read(),d[i].r=read(),d[i].d=i,d[i].ss=0;
        sort(d+1,d+m+1,cmp);
        d[0].l=1,d[0].r=0;
        int l,r,id;
        FOR(i,1,m)
        {
            l=d[i-1].l,r=d[i-1].r;
            if(l<d[i].l) gg[r].emplace_back(l,d[i].l-1,-i);
            if(l>d[i].l) gg[r].emplace_back(d[i].l,l-1,i);
            l=d[i].l;
            if(r<d[i].r) gg[l-1].emplace_back(r+1,d[i].r,-i);
            if(r>d[i].r) gg[l-1].emplace_back(d[i].r+1,r,i);
        }
        unsigned sum=0;
        FOR(i,1,n)
        {
            add(i),sum+=w[i];
            pre[i]=sum+w[i+1]*i-gans(i+1)*2;
            for(auto it:gg[i])
            {
                tie(l,r,id)=it;
                FOR(j,l,r)
                {
                    if(id<0) d[-id].ss-=sum+w[j]*i-gans(j)*2;
                    else d[id].ss+=sum+w[j]*i-gans(j)*2;
                }
            }
        }
        l=1,r=0;
        FOR(i,1,m)
        {
            while(l<d[i].l) d[i].ss+=pre[l-1],l++;
            while(l>d[i].l) d[i].ss-=pre[l-2],l--;
            while(r<d[i].r) d[i].ss+=pre[r],r++;
            while(r>d[i].r) d[i].ss-=pre[r-1],r--;
        }
        FOR(i,1,m) d[i].ss+=d[i-1].ss;
        sort(d+1,d+m+1,cmp1);
        //cout<<num<<endl;
        FOR(i,1,m) printf("%u\n",d[i].ss);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值