bzoj3578: GTY的人类基因组计划2

1 篇文章 0 订阅

Description

GTY召唤了n个人来做实验,GTY家的房子很大,有m个房间一开始所有人都在1号房间里,GTY会命令某人去某个房间等待做实验,或者命令一段区间的房间开始实验,实验会获得一些实验信息点数,点数为房间里的人数,如果一个房间里的一群人已经做过实验了那么这些人将不会增加实验信息点数(不会增加是针对这一群人的,不是对这群人中的每个人,即1,2,3做了实验,1,2再做实验还会增加2点实验点数)

Input

第一行两个整数n,m,q(n,m,q<=10^5)表示人数,房间数和操作数
接下来q行每行一个操作 “C i j”表示让第i个人去房间j “W l r” 表示让区间[l,r]的房间做实验

Output

对于每一个W操作,输出一个数,表示此次操作所获得的实验点数

Sample Input

3 5 7

C 1 2

C 2 2

W 1 2

C 3 2

W 1 2

C 3 3

W 1 3

Sample Output

3

3

0

题解

可以发现,一次操作最多只能让做实验的房间增加2个,也就是说,总共做实验的次数一定不超过2*q,因此,我们可以用set存可以做实验的房间,每次询问实验点数时二分查找一下,时间复杂度O(qlogq)。
现在的问题在于怎么判重。有一种专门针对集合判重的哈希:对每个人赋一个long long的初值,将一个人加入集合就异或这个特征值,从集合中去掉也异或一遍(异或两遍等于没有异或),这样我们用map搞搞就可以很方便的判重了。

#include <cstdio>
#include <cstdlib>
#include <set>
#include <map>
#include <algorithm>

using namespace std;

typedef long long LL;

LL hash[100001],size[100001],a[100001];
int loc[100001];
set <int> s;
map <LL,bool> pd;
char read[2];

int main()
{
//    freopen("test.in","r",stdin);
//    freopen("test1.out","w",stdout);
    srand(200008017);
    int n,m,q;
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++)
    {
        a[i]=((LL)rand()<<16)|rand();
        hash[1]^=a[i];
        loc[i]=1;
    }
    size[1]=n;
    s.insert(1);
    for(int i=1;i<=q;i++)
    {
        LL ans=0;
        scanf("%s",read);
        if(read[0]=='C')
        {
            int x,y;
            scanf("%d%d",&x,&y);
            s.erase(loc[x]);
            s.erase(y);
            hash[loc[x]]^=a[x];
            size[loc[x]]--;
            hash[y]^=a[x];
            size[y]++;
            if(!pd[hash[loc[x]]])s.insert(loc[x]);
            if(!pd[hash[y]])s.insert(y);
            loc[x]=y;
        }
        else
        {
            int l,r;
            scanf("%d%d",&l,&r);
            set<int>::iterator it=lower_bound(s.begin(),s.end(),l);
            //printf("fj:%d ",s.count(1));
            for(;it!=s.end()&&*it<=r;it=lower_bound(s.begin(),s.end(),l))
            {
                pd[hash[*it]]=true;
                s.erase(it);
                ans+=size[*it];
            }
            printf("%lld\n",ans);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值