[bzoj 3460]Jc的宿舍

124 篇文章 2 订阅
5 篇文章 0 订阅

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

一开始看到这道题有取模什么的,会以为是强制在线。
但之后发现它的模数很小,于是可以暴力全部做一遍,然后再特判一下。
现在讲一下正解,其实这道题是一道树上莫队水题(但是本蒟蒻一开始看错了题,浪费了不少时间),我们可以用贪心的思想考虑一下,让排水较快的人先打,慢的人后打,这样可以让总等待时间最小。那么对于区间的修改我们可以用树状数组来统计比它小的数的个数和比它小的数的和,这样就可以维护了。这道题就解决了。注意加long long。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
struct node
{
    int x,y,next;
}a[110000];int len,last[110000];
void ins(int x,int y)
{
    len++;
    a[len].x=x;a[len].y=y;
    a[len].next=last[x];last[x]=len;
}
struct que
{
    int l,r,id;long long s;
}q[110000],c[110000];
int n,m,b[110000],ex[110000];long long t1[110000],t2[110000];
int ss,dfn[110000],f[20][110000],dep[110000],belong[110000];
bool v[110000];
bool cmp1(que a,que b)
{
    if(belong[a.l]==belong[b.l])
    {
        if(dfn[a.r]>dfn[b.r])return false;
        if(dfn[a.r]<dfn[b.r])return true;
        return 0;
    }
    if(belong[a.l]>belong[b.l])return false;
    if(belong[a.l]<belong[b.l])return true;
    return 0;
}
bool cmp2(que a,que b)
{
    if(a.id>b.id)return false;
    if(a.id<b.id)return true;
    return 0;
}
bool cmp3(que a,que b)
{
    if(a.s>b.s)return false;
    if(a.s<b.s)return true;
    return 0;
}
int lowbit(int x){return x&-x;}
void add(int x,int w,int id)
{
    while(x<=n)
    {
        if(id==1)t1[x]+=w;
        else t2[x]+=w;
        x+=lowbit(x);
    }
}
long long getsum(int x,int id)
{
    long long sum=0;
    while(x>=1)
    {
        if(id==1)sum+=t1[x];
        else sum+=t2[x];
        x-=lowbit(x);
    }
    return sum;
} 
void dfs(int x)
{
    dfn[x]=++ss;
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(f[0][x]!=y)
        {
            f[0][y]=x;
            dep[y]=dep[x]+1;
            dfs(y);
        }
    }
}
int lca(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);
    for(int i=15;i>=0;i--)
    {
        if(dep[x]-dep[y]>=(1<<i))x=f[i][x];
    }
    if(x==y)return x;
    for(int i=15;i>=0;i--)
    {
        if(dep[x]>=(1<<i) && f[i][x]!=f[i][y])
        {
            x=f[i][x],y=f[i][y];
        }
    }
    return f[0][x];
}
long long ans1=0,ans2=0;
void work(int x)
{
    if(v[x]==true)
    {
         ans2-=(ans1-getsum(b[x]-1,1))*ex[b[x]];
         ans2-=getsum(b[x]-1,2);
         ans1--;
         add(b[x],-1,1);add(b[x],-ex[b[x]],2);
         v[x]=false;
    }
    else
    {
        add(b[x],1,1);add(b[x],ex[b[x]],2);
        ans1++;
        ans2+=(ans1-getsum(b[x]-1,1))*ex[b[x]];
        ans2+=getsum(b[x]-1,2);
        v[x]=true;
    }
}
void change(int x,int y,int LCA)
{
    while(x!=LCA){work(x);x=f[0][x];}
    while(y!=LCA){work(y);y=f[0][y];}
}
char s1[5];
int main()
{
    memset(v,false,sizeof(v));
    int s=0,key;
    scanf("%d%d%d",&n,&m,&key);
    for(int i=1;i<=n;i++)scanf("%d",&c[i].s),c[i].id=i;
    sort(c+1,c+n+1,cmp3);
    for(int i=1;i<=n;i++)
    {
        b[c[i].id]=i;
        ex[i]=c[i].s;
    }
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d",&x);
        if(x!=0)ins(i,x),ins(x,i);
    }
    int cnt=sqrt(n);dfs(1);
    for(int i=1;i<=n;i++)belong[i]=(dfn[i]-1)/cnt+1;
    for(int i=1;i<=15;i++)
    {
        for(int j=1;j<=n;j++)
        {
            f[i][j]=f[i-1][f[i-1][j]];
        }
    }
    int ll=1;
    for(int i=1;i<=m;i++)
    {
        int x;
        scanf("%s%d",s1+1,&x);
        if(s1[1]=='C')ll=x;
        else
        {
            s++;
            q[s].id=s;q[s].l=ll;q[s].r=x%n+1;
            if(s!=1)
            {
                s++;
                q[s].id=s;q[s].l=ll;q[s].r=(x+key)%n+1;
            }
        }
    }
    sort(q+1,q+s+1,cmp1);
    int l=1,r=1;
    for(int i=1;i<=s;i++)
    {
        change(l,q[i].l,lca(l,q[i].l));
        change(r,q[i].r,lca(r,q[i].r));
        int LCA=lca(q[i].l,q[i].r);
        work(LCA);
        q[i].s=ans2;
        work(LCA);
        l=q[i].l,r=q[i].r;
    }
    sort(q+1,q+s+1,cmp2);
    int pre;
    printf("%lld\n",q[1].s);
    pre=q[1].s%2;
    for(int i=2;i<=s;i+=2)
    {
        if(i%2==pre)
        {
            printf("%lld\n",q[i].s);
            pre=q[i].s%2;
        }
        else
        {
            printf("%lld\n",q[i+1].s);
            pre=q[i+1].s%2;
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值