【2016北京集训】Mushroom
Portal --> broken qwq
Description
一开始有个蘑菇,蘑菇里面有\(n\)个房间,是一棵有根树,\(1\)号是根,每个房间里面都有杂草,现在要支持以下操作:将某个指定蘑菇复制一份作为一个新的蘑菇;将蘑菇\(v\)合并到蘑菇\(u\)中,有杂草的房间取并(合并后\(v\)不会消失);某个蘑菇子树除草/除子树外除草;某个蘑菇路径除草/除路径外除草;某个蘑菇标号为\(l\sim r\)房间除草/除了这些房间外除草;查询清除某个蘑菇上面所有杂草的时间:一单位时间内可以除最多连续\(w\)个下标连续的房间里面的草
数据范围:\(n<=50000,q<=100000,w\in[0,200]\)
Solution
首先吐槽一句这什么鬼题啊==突然想丢出题人蘑菇qwq
首先看那个查询操作,当然是选择贪心啊,每次找到下标最小的有杂草的房间然后往后面跳\(w\)位,再继续找就好了
然后。。前面的呢==维护一个二位的数据结构吗qwq
实际上正解是bitset,注意到每个房间只有两种状态:有杂草或者没有杂草,所以我们可以用\(0\)和\(1\)来表示这两个状态,然后。。这个时候我们就可以快乐压位并且使用强大无比的位运算了
考虑每一个蘑菇开一个bitset,然后复制直接复制,合并就是两个bitset\(\&\)一下,那么其他的各种花式除草怎么搞呢。。注意到花式除草什么的其实我们只要把那一部分的房间状态全部变成\(0\)就好了,然后每个蘑菇内部的树结构是一样的,所以我们可以先预处理出树上每个节点子树内的房间全\(1\)的bitset和树上每个节点到根路径上房间全\(1\)的bitset,然后我们实现一个\(del\)过程:将两个bitset中都为\(1\)的位变为\(0\),那么子树除草直接\(del(subtree)\),子树外除草直接\(\&subtree\),路径除草的两种操作类似,只要先处理出路径上房间全\(1\)的bitset即可(可以用到根路径的bitset亦或起来再强制将\(lca\)那位变为\(1\)),然后至于下标区间修改和查询。。难道暴力跳吗==
这里就出现了问题,所以我们需要手写bitset,因为自带的bitset并不支持大段大段位往后跳的操作
手写bitset的话,实现起来我们可以将\(64\)位压成一个unsigned long long,这样在最后两个与下标相关的操作中,我们就可以像。。分块那样大段大段跳了,写起来。。也和分块有点像(需要注意的是,压位的时候用二进制写出来是二进制的低位是对应排在前面的)
最后这里mark几个用位运算的操作:
a\% 2^n&=a\&(n-1)\\
a*2^n&=a<<n\\
a/2^n&=a>>n
\end{aligned}
\]
但是。。其实还没有完。。
算一下发现空间其实。。有点== 所以这里有一个空间优化的地方就是:因为我们可以离线,所以可以先将那些询问中不需要用到的子树bitset和到根bitset全部delete掉
代码大概长这个样子
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define ull unsigned long long
#define Start(x) (x<<6)
#define End(x) (63+(x<<6))
using namespace std;
const int N=50010,TOP=16,B=64,Div=(1<<16)-1;
struct xxx{
int y,nxt;
}a[N*2];
ull tmprec[N];
int Cnt0[1<<16];
ull lowbit(ull x){return x&-x;}
int CntZero(ull x){//计算写成二进制之后的末尾的0的个数
if (x<=Div) return Cnt0[x];
if (x<=(1LL<<32)-1) return 16+Cnt0[x>>16&Div];
if (x<=(1LL<<48)-1) return 32+Cnt0[x>>32&Div];
return 48+Cnt0[x>>48&Div];
}
struct bset{/*{{{*/
ull *a;
int cntb,cnt1;
void set(int x){a[x>>6]|=1ULL<<(x&63);}
void flip(int x){a[x>>6]^=1ULL<<(x&63);}
void clear(int x){set(x); flip(x);}
void alloc(int len){
if (a!=NULL) Clear();
cntb=(int)ceil(1.0*len/B);
a=new ull[cntb];
memset(a,0,sizeof(ull)*cntb);
int debug=1;
}
void Clear(){
if (a!=NULL){delete a; a=NULL;}
}
void operator |= (const bset &x)const{
for (int i=0;i<cntb;++i) a[i]|=x.a[i];
}
void operator ^= (bset x){
if (x.cntb>cntb) cntb=x.cntb;
for (int i=0;i<cntb;++i)
a[i]^=x.a[i];
}
void operator &= (const bset &x)const{
for (int i=0;i<cntb;++i) a[i]&=x.a[i];
}
void operator = (bset x){
if (a==NULL)
alloc(x.cntb*64);
for (int i=0;i<cntb;++i) a[i]=x.a[i];
}
void del_seg(int l,int r){
if (l>r) return;
int L=l>>6,R=r>>6;
if (L==R)
for (int i=l;i<=r;++i)
clear(i);
else{
for (int i=L+1;i<R;++i) a[i]=0;
a[L]&=(1ULL<<(l-Start(L)))-1;
if (r==End(R))
a[R]=0;
else
a[R]&=~((1ULL<<(r-Start(R))+1)-1);
}
}
void del(bset &x){
for (int i=0;i<cntb;++i) a[i]^=(a[i]&x.a[i]);
}
int query(int w){
int now=0,ret=0,tmp,st,ed;
for (int i=0;i<cntb;++i) tmprec[i]=a[i];
while (now<cntb&&!tmprec[now]) ++now;
while (1){
while (now<cntb&&!tmprec[now]) ++now;
if (now>=cntb) break;
++ret;
st=Start(now)+CntZero(lowbit(tmprec[now]));//有零要跳
ed=st+w;
tmp=ed>>6;
if (ed==End(tmp))
tmprec[tmp]=0,now=tmp+1;
else
tmprec[tmp]&=~((1ULL<<(ed-Start(tmp)+1))-1),now=tmp;
}
return ret;
}
}to_rt[N],subtree[N],rec[N*2];/*}}}*/
struct Q{/*{{{*/
int op,u,x,y;
void read(){
scanf("%d",&op);
if (op==1) scanf("%d",&u);
else if (op<=4||op==9) scanf("%d%d",&u,&x);
else scanf("%d%d%d",&u,&x,&y);
}
}q[N*2];/*}}}*/
int h[N],f[N][TOP+1],dep[N];
bool use_tort[N],use_subtree[N];
int n,m,tot,id;
//tree part{{{
void add(int x,int y){a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot;}
void dfs(int fa,int x,int d){
int u;
to_rt[x]=to_rt[fa]; to_rt[x].set(x-1);
subtree[x].alloc(n); subtree[x].set(x-1);
f[x][0]=fa; dep[x]=d;
for (int i=1;i<=TOP;++i) f[x][i]=f[f[x][i-1]][i-1];
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa) continue;
dfs(x,u,d+1);
subtree[x]|=subtree[u];
}
}
int get_lca(int x,int y){
if (dep[x]<dep[y]) swap(x,y);
for (int i=TOP;i>=0;--i)
if (dep[f[x][i]]>=dep[y]) x=f[x][i];
if (x==y) return x;
for (int i=TOP;i>=0;--i)
if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
}/*}}}*/
void prework(){
Cnt0[0]=1;
for (int i=1;i<16;++i) Cnt0[1<<i]=i;
}
void init(){
memset(h,-1,sizeof(h));
tot=0;
}
void presolve(){
int x,y;
scanf("%d",&n);
for (int i=1;i<n;++i){
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
scanf("%d",&m);
for (int i=1;i<=m;++i){
q[i].read();
if (q[i].op<=2) continue;
if (q[i].op==3||q[i].op==4)
use_subtree[q[i].x]=true;
else if (q[i].op<=6)
use_tort[q[i].x]=true,use_tort[q[i].y]=true;
}
}
void get_info(){
to_rt[0].alloc(n);
dfs(0,1,1);
for (int i=1;i<=n;++i){
if (!use_tort[i]) to_rt[i].Clear();
if (!use_subtree[i]) subtree[i].Clear();
}
}
void print(ull x){
for (int i=0;i<64;++i)
printf("%d",x>>i&1);
}
void debug(bset &x){
for (int i=0;i<x.cntb;++i)
print(x.a[i]);
printf("\n");
}
void solve(){
id=1;
rec[id].alloc(n);
for (int i=1;i<=n;++i) rec[id].set(i-1);
static bset tmp,tmp1;
tmp.alloc(n);
for (int i=1;i<=m;++i){
if (q[i].op==1) rec[++id]=rec[q[i].u];
else if (q[i].op==9)
printf("%d\n",rec[q[i].u].query(q[i].x));
else{
tmp=rec[q[i].u];
if (q[i].op==2)
tmp|=rec[q[i].x];
else if (q[i].op==3)
tmp.del(subtree[q[i].x]);
else if (q[i].op==4)
tmp&=subtree[q[i].x];
else if (q[i].op==7)
tmp.del_seg(q[i].x-1,q[i].y-1);
else if (q[i].op==8){
tmp.del_seg(0,q[i].x-2);
tmp.del_seg(q[i].y,n-1);
}
else{
//debug(tmp);
tmp1=to_rt[q[i].x];
//debug(to_rt[q[i].x]);
tmp1^=to_rt[q[i].y];
//debug(to_rt[q[i].y]);
tmp1.set(get_lca(q[i].x,q[i].y)-1);
if (q[i].op==5)
tmp.del(tmp1);
else
tmp&=tmp1;
//debug(tmp);
}
rec[q[i].u]=tmp;
}
//debug(rec[q[i].u]);
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
prework();
init();
presolve();
get_info();
solve();
}
【2016北京集训】Mushroom的更多相关文章
- (2016北京集训十)【xsy1528】azelso - 概率期望dp
北京集训的题都是好题啊~~(于是我爆0了) 注意到一个重要的性质就是期望是线性的,也就是说每一段的期望步数可以直接加起来,那么dp求出每一段的期望就行了... 设$f_i$表示从$i$出发不回到$i$ ...
- (2016北京集训十三)【xsy1533】mushroom - bitset
题解: 神题...我看到的时候直接吓懵了... 这是一道STL题...否则可能要写可持久化ETT或者可持久化Toptree? 用bitset来维护每个蘑菇上哪里有杂草,那么 对于操作1和操作2:可以预 ...
- 【2016北京集训测试赛(十)】 Azelso (期望DP)
Time Limit: 1000 ms Memory Limit: 256 MB Description 题解 状态表示: 这题的状态表示有点难想...... 设$f_i$表示第$i$个事件经过之 ...
- 【2016北京集训测试赛(二)】 thr (树形DP)
Description 题解 (这可是一道很早就碰到的练习题然后我不会做不想做,没想到在Contest碰到欲哭无泪......) 题目大意是寻找三点对的个数,使得其中的三个点两两距离都为d. 问题在于 ...
- 【2016北京集训测试赛(八)】 crash的数列 (思考题)
Description 题解 题目说这是一个具有神奇特性的数列!这句话是非常有用的因为我们发现,如果套着这个数列的定义再从原数列引出一个新数列,它居然还是一样的...... 于是我们就想到了能不能用多 ...
- 【2016北京集训测试赛(十六)】 River (最大流)
Description Special Judge Hint 注意是全程不能经过两个相同的景点,并且一天的开始和结束不能用同样的交通方式. 题解 题目大意:给定两组点,每组有$n$个点,有若干条跨组 ...
- 【2016北京集训测试赛】river
HINT 注意是全程不能经过两个相同的景点,并且一天的开始和结束不能用同样的交通方式. [吐槽] 嗯..看到这题的想法的话..先想到了每个点的度为2,然后就有点不知所措了 隐隐约约想到了网络流,但并没 ...
- 【2016北京集训测试赛】azelso
[吐槽] 首先当然是要orzyww啦 以及orzyxq奇妙顺推很强qwq 嗯..怎么说呢虽然说之前零零散散做了一些概d的题目但是总感觉好像并没有弄得比较明白啊..(我的妈果然蒟蒻) 这题的话可以说是难 ...
- [2016北京集训测试赛17]crash的游戏-[组合数+斯特林数+拉格朗日插值]
Description Solution 核心思想是把组合数当成一个奇怪的多项式,然后拉格朗日插值..:哦对了,还要用到第二类斯特林数(就是把若干个球放到若干个盒子)的一个公式: $x^{n}=\su ...
随机推荐
- 概念这种东西--node.js
概念是一个既简单又复杂.既招人爱又招人恨的东西.概念是对一事务或现象的抽象.抽象好了,那就太方便问题的解决了,抽象坏了,那就驴唇不对马嘴,反而会让逻辑一塌糊涂.现实中经常有这样的概念:东北人怎么怎么样 ...
- Linux命令之tar命令
[root@linux ~]# tar [-cxtzjvfpPN] 文件与目录 .... 参数: -c :建立一个压缩文件的参数指令(create 的意思): -x :解开一个压缩文件的参数指令! - ...
- http 502 bad gate way
世界杯期间,公司的cdn在回源时突然出现大量502. 刚出现问题时,因为考虑到一般502都是上游服务器出现问题,然后因为已经服务了很久都没有出现过这种问题, 就没有仔细考虑,就让回源中心的同事进行排查 ...
- IO多路复用(一)-- Select、Poll、Epoll
在上一篇博文中提到了五种IO模型,关于这五种IO模型可以参考博文IO模型浅析-阻塞.非阻塞.IO复用.信号驱动.异步IO.同步IO,本篇主要介绍IO多路复用的使用和编程. IO多路复用的概念 多路复用 ...
- leetcode28_C++实现strStr()函数
实现 strStr() 函数. 给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始).如果不存在,则返 ...
- sshpass 指定密码远程 ssh 到服务器 或者 scp 发送文件到服务器
在操作linux时,虽然可以对linux配置免秘钥登录,但是在配置免密码登录之前,是需要登录到其他节点主机的,这里提供一种类似ssh的方式,可以在命令后面加上相应的参数来设置你将要登录的远程主机的密码 ...
- openresty 安装指南
对于一些常见的 Linux 发行版本,OpenResty 提供 官方预编译包.确保你首先用这种方式来安装. 如果您还没有下载 OpenResty 的源码包, 请到 Download 页下载. 首先,您 ...
- BZOJ 1901 Zju2112 Dynamic Rankings 树状数组套线段树
题意概述:带修改求区间第k大. 分析: 我们知道不带修改的时候直接上主席树就可以了对吧?两个版本号里面的节点一起走在线段树上二分,复杂度是O((N+M)logN). 然而这里可以修改,主席树显然是凉了 ...
- iframe高度自适应的6个方法
原文链接:http://caibaojian.com/iframe-adjust-content-height.html JS自适应高度,其实就是设置iframe的高度,使其等于内嵌网页的高度,从而看 ...
- Codeforces Round #312 (Div. 2) E. A Simple Task 线段树+计数排序
题目链接: http://codeforces.com/problemset/problem/558/E E. A Simple Task time limit per test5 secondsme ...