【GDOI2017 day1】取石子游戏 线段树+区间合并
题面
如果给你一棵有根树,树根为 1,并且树的每个结点上有一个权值,现在我想知道每个点,除它所在子树以外的结点权值集合的 mex,怎么做呢?
在这里,mex 是定义在集合上的函数,mex(S) 表示 S 这个集合中,最小的非负整数喔。
对于 20% 的数据:N ≤ 500, T ≤ 20
另外 50% 的数据:N ≤ 100000, T ≤ 5
最后 30% 的数据:N ≤ 1000000, T ≤ 1
100
\(O(nlogn)\)
考虑把树上问题变为序列上的问题:
每次询问相当于是抠掉dfs序列中的一段区间,然后询问前缀后缀;
考虑给序列复制一遍,那么前缀和后缀拼起来就变成了一个区间。
然后就变成了序列上的问题了。
【清华集训2014】mex
有一个长度为n的数组{a1,a2,...,an}。m次询问,每次询问一个区间内最小没有出现过的自然数。
我们构出权值主席树,每一位表示这一数字至此最后出现的位置。
那么显然有种二分的做法。
同时,离线可以把主席树变成线段树。
Back to Problem
然后这道题就等价于上述的那道题了。
\(O(n)\)
枚举i从0到n,考虑那些树上结点的答案是当前枚举的i。
我们给所有权值=i的结点做个LCA,这个LCA到根节点的路径上未赋值的答案都为i。
然后利用并查集和tarjan求LCA,这是可以做到\(O(n)\)(\(O(n*\alpha)\))
Code
正确性未知,可能会被卡常的\(O(nlogn)\)做法。
#include<bits/stdc++.h>
#define ll long long
#define fo(i,x,y) for(int i=x;i<=y;i++)
#define fd(i,x,y) for(int i=x;i>=y;i--)
using namespace std;
const int inf=0x7fffffff;
const char* fin="2.in";
const char* fout="2.out";
const int maxn=1000007,maxm=maxn*2,maxt=maxn*4;
int n,m,fi[maxn],la[maxm],ne[maxm],tot,num,a[maxn],w[maxm];
int b[maxn][2],hd,tl,dfn[maxn],ffn[maxn],si[maxn];
int c[maxt];
struct Q{int l,r,id;}q[maxn];
bool cmp(const Q &a,const Q &b){return a.r<b.r;}
void modify(int l,int r,int t,int v,int vv){
int mid=(l+r)/2;
if (l==r){c[t]=vv;return;}
if (v<=mid) modify(l,mid,t*2,v,vv);
else modify(mid+1,r,t*2+1,v,vv);
c[t]=(c[t*2]>c[t*2+1]?c[t*2+1]:c[t*2]);
}
int query(int l,int r,int t,int v){
int mid=(l+r)/2;
if (l==r) return l;
if (c[t*2]<v) return query(l,mid,t*2,v);
else return query(mid+1,r,t*2+1,v);
}
void add_line(int a,int b){
tot++;
ne[tot]=fi[a];
la[tot]=b;
fi[a]=tot;
}
int read(){
int x=0;
char ch=getchar();
while (ch<'0' || ch>'9') ch=getchar();
while (ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
void write(int x){
int w[20];
w[0]=0;
while (x) w[++w[0]]=x%10,x/=10;
fd(i,w[0],1) putchar(w[i]+'0');
}
void bfs(int v){
hd=tl=0;
b[++tl][0]=v;
b[tl][1]=0;
while (hd++<tl)
for(int k=fi[b[hd][0]];k;k=ne[k])
if (la[k]!=b[hd][1]) b[++tl][0]=la[k],b[tl][1]=b[hd][0];
}
void getdfn(){
bfs(1);
fd(i,tl,1){
int x=b[i][0],y=b[i][1];
si[x]=1;
for(int k=fi[x];k;k=ne[k]) if (la[k]!=y) si[x]+=si[la[k]];
}
fo(i,1,tl){
int x=b[i][0],y=b[i][1];
dfn[x]=ffn[x]=ffn[y]+1;
ffn[y]+=si[x];
}
}
void pre(){
scanf("%d%d",&n,&m);
tot=0;
memset(fi,0,sizeof fi);
memset(ffn,0,sizeof ffn);
fo(i,1,n) a[i]=read();
fo(i,1,m){
int j=read(),k=read();
add_line(j,k);
add_line(k,j);
}
}
int ans[maxn];
void solve(){
fo(i,1,n) w[dfn[i]]=w[dfn[i]+n]=a[i]+1;
fo(i,1,4*n) c[i]=0;
fo(i,1,n) q[i].l=dfn[i]+si[i],q[i].r=dfn[i]+n-1,q[i].id=i;
sort(q+1,q+n+1,cmp);
int j=1;
fo(i,1,n){
while (j<=q[i].r) modify(1,maxn,1,w[j],j),j++;
ans[q[i].id]=query(1,maxn,1,q[i].l)-1;
}
fo(i,1,n)write(ans[i]),putchar(' ');
printf("\n");
}
int main(){
freopen(fin,"r",stdin);
freopen(fout,"w",stdout);
int t=read();
while (t--){
pre();
getdfn();
solve();
}
return 0;
}
【GDOI2017 day1】取石子游戏 线段树+区间合并的更多相关文章
- HDU 3911 线段树区间合并、异或取反操作
题目:http://acm.hdu.edu.cn/showproblem.php?pid=3911 线段树区间合并的题目,解释一下代码中声明数组的作用: m1是区间内连续1的最长长度,m0是区间内连续 ...
- POJ 3667 Hotel(线段树 区间合并)
Hotel 转载自:http://www.cnblogs.com/scau20110726/archive/2013/05/07/3065418.html [题目链接]Hotel [题目类型]线段树 ...
- HDU 3911 Black And White(线段树区间合并+lazy操作)
开始以为是水题,结果...... 给你一些只有两种颜色的石头,0为白色,1为黑色. 然后两个操作: 1 l r 将[ l , r ]内的颜色取反 0 l r 计算[ l , r ]内最长连续黑色石头的 ...
- HYSBZ 1858 线段树 区间合并
//Accepted 14560 KB 1532 ms //线段树 区间合并 /* 0 a b 把[a, b]区间内的所有数全变成0 1 a b 把[a, b]区间内的所有数全变成1 2 a b 把[ ...
- 【bzoj2653】middle 可持久化线段树区间合并
题目描述 一个长度为n的序列a,设其排过序之后为b,其中位数定义为b[n/2],其中a,b从0开始标号,除法取下整.给你一个长度为n的序列s.回答Q个这样的询问:s的左端点在[a,b]之间,右端点在[ ...
- 【bzoj1858】[Scoi2010]序列操作 线段树区间合并
题目描述 lxhgww最近收到了一个01序列,序列里面包含了n个数,这些数要么是0,要么是1,现在对于这个序列有五种变换操作和询问操作: 0 a b 把[a, b]区间内的所有数全变成0 1 a b ...
- hdu 3308(线段树区间合并)
LCIS Time Limit: 6000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submis ...
- 【BZOJ3638】Cf172 k-Maximum Subsequence Sum 线段树区间合并(模拟费用流)
[BZOJ3638]Cf172 k-Maximum Subsequence Sum Description 给一列数,要求支持操作: 1.修改某个数的值 2.读入l,r,k,询问在[l,r]内选不相交 ...
- 【bzoj3638】Cf172 k-Maximum Subsequence Sum 模拟费用流+线段树区间合并
题目描述 给一列数,要求支持操作: 1.修改某个数的值 2.读入l,r,k,询问在[l,r]内选不相交的不超过k个子段,最大的和是多少. 输入 The first line contains inte ...
随机推荐
- Django项目: 项目环境搭建 ---- 二、django项目设置
1.配置模板文件 TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.p ...
- ES6之主要知识点(九)Set和Map
1.Set ES6 提供了新的数据结构 Set.它类似于数组,但是成员的值都是唯一的,没有重复的值. Set 本身是一个构造函数,用来生成 Set 数据结构. const s = new Set(); ...
- Java 多线程同步和异步详解
java线程 同步与异步 线程池 1)多线程并发时,多个线程同时请求同一个资源,必然导致此资源的数据不安全,A线程修改了B线 程的处理的数据,而B线程又修改了A线程处理的数理.显然这是由于全局资源造成 ...
- 在VisualSVN创建新的Repository
1,右键-创建 2,(暂时还没明白这两个选项的区别,暂时先使用第一个) 3, 库名称 4,(也不懂具体含义, 先默认) 5, 权限问题 6, 在需要用到这个库的工作文件夹内点击右键: -SVN检出 7 ...
- 很好用的API管理--Swagger
1.打开NuGet程序包 2.安装下面两个程序包 3.安装完后会出现SwaggerConfig.cs类,并修改里面的内容 代码: [assembly: PreApplicationStartMetho ...
- 【hihocoder 1477】闰秒
[题目链接]:http://hihocoder.com/problemset/problem/1477 [题意] 中文题 [题解] 首先,一年一年地加,把开始的年份和结束的年份之间的年根据是否为闰年; ...
- LUOGU P2986 [USACO10MAR]伟大的奶牛聚集Great Cow Gat…
传送门 解题思路 首先第一遍dfs预处理出每个点的子树的siz,然后可以处理出放在根节点的答案,然后递推可得其他答案,递推方程 sum[u]=sum[x]-(val[i]*siz[u])+(siz[1 ...
- 【一坨理论AC的题】Orz sxy大佬
1.UVA10891 Game of Sum 2.LA4254 Processor . 3.UVA10905 Children's Game 4.UVA11389 The Bus Driver Pro ...
- 微信小程序注册使用流程
新开发微信小程序使用流程: 平时使用小程序较多,但是具体注册流程简单记录下: 第一步:通过邮箱注册 第二步:在邮箱进行激活 注册后,在邮箱会收到激活信息提示.点击激活地址进行激活. 第三步:信息登记 ...
- 90 k数和 II
原题网址:https://www.lintcode.com/problem/k-sum-ii/description 描述 Given n unique integers, number k (1&l ...