入门的一篇讲解,很棒:http://www.mamicode.com/info-detail-2600077.html
题目练习:https://blog.csdn.net/qq_34940287/article/details/87718729
K-th Number
Poj 2104
查询区间第k小
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=100010;
inline int read()
{
int res=0;
char ch;
while((ch=getchar())<'0'||ch>'9');
while(ch>='0'&&ch<='9') res=(res<<3)+(res<<1)+ch-'0',ch=getchar();
return res;
}
int ls[maxn*20],rs[maxn*20],sum[maxn*20],root[maxn];
int n,m,a[maxn],b[maxn],cnt;
void update(int &now,int old,int l,int r,int pos)
{
now=++cnt;
ls[now]=ls[old];rs[now]=rs[old];
sum[now]=sum[old]+1;
if(l==r) return;
int mid=(l+r)>>1;
if(pos<=mid) update(ls[now],ls[old],l,mid,pos);
else update(rs[now],rs[old],mid+1,r,pos);
}
int query(int x,int y,int l,int r,int val)
{
if(l==r) return l;
int c=sum[ls[y]]-sum[ls[x]];
int mid=(l+r)>>1;
if(val<=c) return query(ls[x],ls[y],l,mid,val);
else return query(rs[x],rs[y],mid+1,r,val-c);
}
int main()
{
while(~scanf("%d%d",&n,&m)){
for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i];
sort(b+1,b+n+1);
int sz=unique(b+1,b+n+1)-b-1;
int l,r,x;
cnt=0;
for(int i=1;i<=n;i++){
int pos=lower_bound(b+1,b+sz+1,a[i])-b;//
update(root[i],root[i-1],1,sz,pos);
}
for(int i=0;i<m;i++){
scanf("%d%d%d",&l,&r,&x);
int ans=b[query(root[l-1],root[r],1,sz,x)];
printf("%d\n",ans);
}
}
return 0;
}
/*
7 3
1 5 2 6 3 7 4
2 5 3
4 4 1
1 7 3
*/
Count on a tree(可持续化线段树(主席树)树上第k小)
SPOJ - COT
题意:给定一棵树,q个查询,求路径u-v上第k小
参考代码:https://blog.csdn.net/qq_41730082/article/details/86652202
#include<cstring>
#include<cstdio>
#include<iostream>
#include<cmath>
#include<queue>
#include<algorithm>
using namespace std;
#define ll long long
const int maxn=100010;
struct edge{
int nxt,v;
}e[maxn*2];
int head[maxn],tot;
int n,m,a[maxn],b[maxn],num;
int f[maxn][20],deep[maxn];
int order,tree[maxn],lson[maxn*20],rson[maxn*20],sz[maxn*20];
inline void init()
{
memset(head,-1,sizeof(head));
order=tot=0;
memset(f,-1,sizeof(f));
}
void addedge(int u,int v)
{
e[tot].v=v;e[tot].nxt=head[u];
head[u]=tot++;
}
void dfs(int u,int pre,int d)
{
f[u][0]=pre;
deep[u]=d;
for(int i=head[u];~i;i=e[i].nxt){
int v=e[i].v;
if(v==pre) continue;
dfs(v,u,d+1);
}
}
void pre_lca()
{
dfs(1,0,0);
for(int j=0;(1<<(j+1))<n;j++){
for(int i=1;i<=n;i++){
if(f[i][j]<0) f[i][j+1]=-1;
else f[i][j+1]=f[f[i][j]][j];
}
}
}
int get_lca(int x,int y)
{
if(deep[x]<deep[y]) swap(x,y);
int det=deep[x]-deep[y];
for(int i=0;(1<<i)<=det;i++) if(det&(1<<i)) x=f[x][i];
if(x==y) return x;
for(int i=log2(1.0*n);i>=0;i--){
if(f[x][i]!=f[y][i]){
x=f[x][i];y=f[y][i];
}
}
return f[x][0];
}
void build(int &k,int l,int r)
{
k=++order;
sz[k]=0;
if(l==r) return;
int m=(l+r)>>1;
build(lson[k],l,m);
build(rson[k],m+1,r);
}
void update(int old,int &now,int l,int r,int qx)
{
now=++order;
lson[now]=lson[old];rson[now]=rson[old];sz[now]=sz[old]+1;
if(l==r) return;
int m=(l+r)>>1;
if(qx<=m) update(lson[old],lson[now],l,m,qx);
else update(rson[old],rson[now],m+1,r,qx);
}
int query(int i,int j,int l,int r,int k,int lca,int fsa)
{
if(l==r) return l;
int det=sz[lson[i]]+sz[lson[j]]-sz[lson[lca]]-sz[lson[fsa]];
int m=(l+r)>>1;
if(k<=det) return query(lson[i],lson[j],l,m,k,lson[lca],lson[fsa]);
else return query(rson[i],rson[j],m+1,r,k-det,rson[lca],rson[fsa]);
}
void dfs_build(int u,int pre)
{
int pos=(int)(lower_bound(b+1,b+num+1,a[u])-b);
update(tree[pre],tree[u],1,num,pos);
for(int i=head[u];~i;i=e[i].nxt){
int v=e[i].v;
if(v==pre) continue;
dfs_build(v,u);
}
}
int main()
{
while(~scanf("%d%d",&n,&m)){
init();
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);b[i]=a[i];
}
sort(b+1,b+n+1);
num=unique(b+1,b+n+1)-b-1;
int u,v;
for(int i=1;i<n;i++){
scanf("%d%d",&u,&v);
addedge(u,v);addedge(v,u);
}
pre_lca();
build(tree[0],1,num);
dfs_build(1,0);
for(int i=1;i<=m;i++){
int l,r,k;scanf("%d%d%d",&l,&r,&k);
int lca=get_lca(l,r);
int pos=query(tree[l],tree[r],1,num,k,tree[lca],tree[f[lca][0]]);
printf("%d\n",b[pos]);
}
}
return 0;
}
最大异或和
Bzoj 3261
题解:http://hzwer.com/5634.html
#include<bits/stdc++.h>
using namespace std;
const int maxn=600010;
inline int read()
{
int res=0;
char ch;
while((ch=getchar())<'0'||ch>'9');
while(ch>='0'&&ch<='9') res=(res<<3)+(res<<1)+ch-'0',ch=getchar();
return res;
}
int bin[30];
int n,m;
int a[maxn],b[maxn],root[maxn];
struct Trie{
int cnt;
int ch[maxn*24][2],sum[maxn*24];
int insert(int x,int val){
int tmp,y;tmp=y=++cnt;
for(int i=23;i>=0;i--){
ch[y][0]=ch[x][0];ch[y][1]=ch[x][1];
sum[y]=sum[x]+1;
int t=val&bin[i];t>>=i;
x=ch[x][t];
ch[y][t]=++cnt;
y=ch[y][t];
}
sum[y]=sum[x]+1;
return tmp;
}
int query(int l,int r,int val){
int tmp=0;
for(int i=23;i>=0;i--){
int t=val&bin[i];t>>=i;
if(sum[ch[r][t^1]]-sum[ch[l][t^1]])
tmp+=bin[i],r=ch[r][t^1],l=ch[l][t^1];
else r=ch[r][t],l=ch[l][t];
}
return tmp;
}
}trie;
int main()
{
bin[0]=1;
for(int i=1;i<30;i++) bin[i]=bin[i-1]<<1;
n=read();m=read();
n++;
for(int i=2;i<=n;i++) a[i]=read();
for(int i=1;i<=n;i++) b[i]=b[i-1]^a[i];
for(int i=1;i<=n;i++)
root[i]=trie.insert(root[i-1],b[i]);
char ch[4];
int l,r,x;
while(m--){
scanf("%s",ch);
if(ch[0]=='A'){
n++;a[n]=read();b[n]=b[n-1]^a[n];
root[n]=trie.insert(root[n-1],b[n]);
}else{
l=read();r=read();x=read();
printf("%d\n",trie.query(root[l-1],root[r],b[n]^x));
}
}
return 0;
}
/*
5 5
2 6 4 3 6
A 1
Q 3 5 4
A 4
Q 5 7 0
Q 3 6 6
*/
[POI2014]KUR-Couriers
P3567
题意是嫖的:https://www.cnblogs.com/winmt/p/6607392.html
给一个长度为n的序列a。1≤a[i]≤n。
m组询问,每次询问一个区间[l,r],是否存在一个数在[l,r]中出现的次数大于(r-l+1)/2。如果存在,输出这个数,否则输出0。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=5000010;
int sum[maxn*20],ls[maxn*20],rs[maxn*20];
int root[maxn],n,m,cnt;
void update(int &now,int old,int l,int r,int pos)
{
now=++cnt;
sum[now]=sum[old]+1;
if(l==r) return;
ls[now]=ls[old];rs[now]=rs[old];
int mid=(l+r)>>1;
if(pos<=mid) update(ls[now],ls[old],l,mid,pos);
else update(rs[now],rs[old],mid+1,r,pos);
}
int query(int x,int y,int l,int r,int val)
{
if(l==r) return l;
int mid=(l+r)>>1;
if(sum[ls[y]]-sum[ls[x]]>val) return query(ls[x],ls[y],l,mid,val);
if(sum[rs[y]]-sum[rs[x]]>val) return query(rs[x],rs[y],mid+1,r,val);
return 0;
}
int main()
{
while(~scanf("%d%d",&n,&m)){
int l,r,x;
cnt=0;
for(int i=1;i<=n;i++){
scanf("%d",&x);
update(root[i],root[i-1],1,n,x);
}
while(m--){
scanf("%d%d",&l,&r);
int ans=query(root[l-1],root[r],1,n,(r-l+1)/2);
printf("%d\n",ans);
}
}
}
[CQOI2015]任务查询系统
P3168
参考:https://www.cnblogs.com/TheRoadToTheGold/p/6366165.html
题意:n个任务,给定持续时间s,e以及优先级p;m次查询,查询第x个时间上前k小优先级之和。
题解:以时间建立n棵线段树,线段树上以优先级维护优先级之和,以及优先级数量;优先级要离散化。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
const int maxn=200010;
int ls[maxn*20],rs[maxn*20],num[maxn*20];
ll sum[maxn*20];
int root[maxn],n,m,cnt;
int b[maxn],p[maxn];
struct node{
int pos,co,val;
}a[maxn];
bool cmp(const node &x,const node &y)
{
return x.pos<y.pos;
}
int tot;
void init()
{
sort(b+1,b+2*n+1);
tot=unique(b+1,b+2*n+1)-b-1;
for(int i=1;i<=n;i++){
p[i]=lower_bound(b+1,b+tot+1,p[i])-b;
a[i*2-1].val=a[i*2].val=p[i];
}
}
void update(int &now,int old,int l,int r,int w,int c)
{
now=++cnt;
sum[now]=sum[old]+1LL*b[w]*c;
num[now]=num[old]+c;//不是 + 1
ls[now]=ls[old];rs[now]=rs[old];
if(l==r) return;
int mid=(l+r)>>1;
if(w<=mid) update(ls[now],ls[old],l,mid,w,c);
else update(rs[now],rs[old],mid+1,r,w,c);
}
ll query(int x,int l,int r,int k)
{
if(l==r){
if(num[x]==0) return 0;
return sum[x]/num[x]*k;
}
int mid=(l+r)>>1;
if(k<=num[ls[x]]) return query(ls[x],l,mid,k);
return query(rs[x],mid+1,r,k-num[ls[x]])+sum[ls[x]];
}
int main()
{
while(~scanf("%d%d",&n,&m)){
for(int i=1;i<=n;i++){
scanf("%d%d%d",&a[i*2-1].pos,&a[i*2].pos,&p[i]);
a[i*2].pos++;
b[i]=p[i];
a[i*2-1].co=1;a[i*2].co=-1;
}
init();
sort(a,a+2*n+1,cmp);
cnt=0;
memset(sum,0,sizeof(sum));
memset(num,0,sizeof(num));
for(int i=1,j=1;i<=m;i++){
root[i]=root[i-1];//
while(j<=2*n&&a[j].pos==i){
update(root[i],root[i],1,tot,a[j].val,a[j].co);
j++;
}
}
int x,a,b,c,k;
ll pre=1;
for(int i=1;i<=m;i++){
scanf("%d%d%d%d",&x,&a,&b,&c);
k=1+(1LL*a*pre+b)%c;
pre=query(root[x],1,tot,k);
printf("%lld\n",pre);
}
}
return 0;
}
可持续化数组
可持续化数组板子题:https://www.luogu.org/problem/P3919
参考代码:https://blog.csdn.net/chenxiaoran666/article/details/81503323
#include<bits/stdc++.h>
using namespace std;
const int maxn=1000010;
int n,q,a[maxn],rt[maxn],cnt;
int val[maxn*20],ls[maxn*20],rs[maxn*20];
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
void build(int &now,int l,int r)
{
now=++cnt;
int mid=(l+r)>>1;
if(l==r){
val[now]=a[l];return;
}
build(ls[now],l,mid);build(rs[now],mid+1,r);
}
void update(int &now,int old,int l,int r,int x,int y)
{
now=++cnt;
val[now]=val[old];
ls[now]=ls[old];rs[now]=rs[old];
if(l==r){
val[now]=y;return;
}
int mid=(l+r)>>1;
if(x<=mid) update(ls[now],ls[old],l,mid,x,y);
else update(rs[now],rs[old],mid+1,r,x,y);
}
int query(int now,int l,int r,int x)
{
int mid=(l+r)>>1;
if(l==r) return val[now];
if(x<=mid) return query(ls[now],l,mid,x) ;
else return query(rs[now],mid+1,r,x);
}
int main()
{
n=read();q=read();
for(int i=1;i<=n;++i) a[i]=read();
cnt=0;
build(rt[0],1,n);
int v,op,x,y;
for(int i=1;i<=q;i++){
v=read();op=read();x=read();
if(op==1) y=read(),update(rt[i],rt[v],1,n,x,y);
else printf("%d\n",query(rt[i]=rt[v],1,n,x));
}
return 0;
}
可持续化并查集
板子题:https://www.luogu.org/problem/P3402
参考代码:https://blog.csdn.net/chenxiaoran666/article/details/81505870
#include<bits/stdc++.h>
using namespace std;
const int maxn=200010;
int n,q,a[maxn],rt[maxn],cnt;
int fa[maxn*20],ls[maxn*20],rs[maxn*20];
int sz[maxn*20];
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
void build(int &now,int l,int r)
{
now=++cnt;
int mid=(l+r)>>1;
if(l==r){
fa[now]=l;return;
}
build(ls[now],l,mid);build(rs[now],mid+1,r) ;
}
void update(int &now,int old,int l,int r,int x,int p)
{
now=++cnt;
if(l==r){
fa[now]=p;sz[now]=sz[old];return;
}
int mid=(l+r)>>1;
ls[now]=ls[old];rs[now]=rs[old];
if(x<=mid) update(ls[now],ls[old],l,mid,x,p);
else update(rs[now],rs[old],mid+1,r,x,p);
}
void Add_sz(int now,int l,int r,int x)
{
if(l==r){
++sz[now];return;
}
int mid=(l+r)>>1;
if(x<=mid) Add_sz(ls[now],l,mid,x);
else Add_sz(rs[now],mid+1,r,x);
}
int query(int now,int l,int r,int x)
{
if(l==r) return now;
int mid=(l+r)>>1;
if(x<=mid) return query(ls[now],l,mid,x);
else return query(rs[now],mid+1,r,x);
}
int find1(int now,int x)
{
int p=query(now,1,n,x);
return fa[p]==x?p:find1(now,fa[p]);
}
void connect(int v,int x,int y)
{
int fx=find1(rt[v],x),fy=find1(rt[v],y);
if(fx==fy) return;
if(sz[fx]<sz[fy]) swap(fx,fy);
update(rt[v],rt[v-1],1,n,fa[fy],fa[fx]);
if(sz[fx]==sz[fy]) Add_sz(rt[v],1,n,fa[fx]);
}
int main()
{
n=read();q=read();
cnt=0;
build(rt[0],1,n);
for(int i=1;i<=q;++i){
int op,x,y;
op=read();x=read();
if(op^2) y=read(),rt[i]=rt[i-1];
switch(op){
case 1:connect(i,x,y);break;
case 2:rt[i]=rt[x];break;
case 3:{
if((find1(rt[i],x)==find1(rt[i],y))) puts("1");
else puts("0");
break;
}
}
}
return 0;
}
[SDOI2010]粟粟的书架
P2468
原题链接:https://www.luogu.org/problem/P2468
参考:https://www.cnblogs.com/neighthorn/p/6400438.html
题解:小数据部分用二维前缀和,大数据部分用裸的主席树。
#include<bits/stdc++.h>
using namespace std;
const int maxn=210;
const int maxm=1010;
const int N=500010;
const int M=7000010;
int n,m,q,p[maxn][maxn],f[maxn][maxn][maxm];
int g[maxn][maxn][maxm];
int cnt,G[N],ls[M],rs[M],sum[M],num[M],root[N];
void solve1()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&p[i][j]);
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
for(int k=1;k<=1000;k++)
f[i][j][k]=f[i][j-1][k]+f[i-1][j][k]-f[i-1][j-1][k],
g[i][j][k]=g[i][j-1][k]+g[i-1][j][k]-g[i-1][j-1][k];
f[i][j][p[i][j]]+=p[i][j],g[i][j][p[i][j]]++;
}
for(int k=999;k>=1;k--)
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
f[i][j][k]+=f[i][j][k+1],g[i][j][k]+=g[i][j][k+1];
for(int i=1,x1,y1,x2,y2,h;i<=q;i++){
scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&h);
int l=1,r=1000,ans=-1;
while(l<=r){
int mid=(l+r)>>1;
if(f[x2][y2][mid]-f[x1-1][y2][mid]-f[x2][y1-1][mid]+f[x1-1][y1-1][mid]>=h)
ans=mid,l=mid+1;
else
r=mid-1;
}
if(ans==-1)
puts("Poor QLW");
else{
int cnt=g[x2][y2][ans]-g[x1-1][y2][ans]-g[x2][y1-1][ans]+g[x1-1][y1-1][ans];
int tmp=(g[x2][y2][ans]-g[x2][y2][ans+1])-(g[x1-1][y2][ans]-g[x1-1][y2][ans+1])
-(g[x2][y1-1][ans]-g[x2][y1-1][ans+1])+(g[x1-1][y1-1][ans]-g[x1-1][y1-1][ans+1]);
int num=f[x2][y2][ans]-f[x1-1][y2][ans]-f[x2][y1-1][ans]+f[x1-1][y1-1][ans];
cnt-=tmp;
while(tmp&&num-ans>=h) tmp--,num-=ans;
printf("%d\n",cnt+tmp);
}
}
}
void update(int &now,int old,int l,int r,int pos)
{
now=++cnt;
sum[now]=sum[old]+pos;num[now]=num[old]+1;
ls[now]=ls[old];rs[now]=rs[old];
if(l==r) return;
int mid=(l+r)>>1;
if(pos<=mid) update(ls[now],ls[old],l,mid,pos);
else update(rs[now],rs[old],mid+1,r,pos);
}
int query(int now,int old,int l,int r,int val)
{
if(l==r) return (val-1)/l+1;
int mid=(l+r)>>1;
if(sum[rs[now]]-sum[rs[old]]>=val)
return query(rs[now],rs[old],mid+1,r,val);
else
return query(ls[now],ls[old],l,mid,val-(sum[rs[now]]-sum[rs[old]]))+num[rs[now]]-num[rs[old]];
}
void solve2()
{
n=m;cnt=0;
memset(sum,0,sizeof(sum));
memset(num,0,sizeof(num));
for(int i=1,x;i<=n;i++)
scanf("%d",&x),G[i]=G[i-1]+x,update(root[i],root[i-1],1,1000,x);
for(int i=1,x1,y1,x2,y2,h;i<=q;i++){
scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&h);
if(G[y2]-G[y1-1]<h) puts("Poor QLW");
else printf("%d\n",query(root[y2],root[y1-1],1,1000,h));
}
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
if(n!=1) solve1();
else solve2();
return 0;
}
[CTSC2018]混合果汁
P4602
题目链接:https://www.luogu.org/problem/P4602
参考:https://www.cnblogs.com/HocRiser/p/9046588.html
题解:二分美味度,建多棵美味度线段树,以价格为权值。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=100010;
const int mx=100000;
const int maxm=1000010;
struct node{
int d,p,l;
}a[maxn];
bool cmp(const node &x,const node &y)
{
return x.d<y.d;
}
int b[maxn];//美味度
ll c[maxn];//c[i]表 美味度>=i 有多少升
int n,m;
ll sum[maxn*20],sz[maxn*20];
int root[maxn*20],ls[maxn*20],rs[maxn*20];
int cnt;
void update(int &now,int old,int l,int r,int pos,int val)//以价格建权值树
{
now=++cnt;
sum[now]=sum[old];sz[now]=sz[old];
ls[now]=ls[old];rs[now]=rs[old];
if(l==r){
sum[now]+=1LL*l*val;sz[now]+=val;return;
}
int mid=(l+r)>>1;
if(pos<=mid) update(ls[now],ls[old],l,mid,pos,val);
else update(rs[now],rs[old],mid+1,r,pos,val);
sum[now]=sum[ls[now]]+sum[rs[now]];
sz[now]=sz[ls[now]]+sz[rs[now]];
}
ll query(int now,int l,int r,ll val)
{
if(l==r) return 1LL*l*val;
int mid=(l+r)>>1;
if(sz[ls[now]]>=val) return query(ls[now],l,mid,val);
else return sum[ls[now]]+query(rs[now],mid+1,r,val-sz[ls[now]]);
}
ll g,h;
bool ok(int x)
{
if(c[x]<h) return 0;
ll tmp=query(root[x],1,mx,h);
return tmp<=g;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d%d%d",&a[i].d,&a[i].p,&a[i].l);
}
sort(a+1,a+n+1,cmp);
for(int i=mx,j=n;i>=1;i--){
c[i]=c[i+1];
while(j>=1&&a[j].d>=i) c[i]+=a[j].l,j--;
}
cnt=0;
for(int i=mx,j=n;i>=1;i--){//美味度建线段树
root[i]=root[i+1];
while(j>=1&&a[j].d>=i) update(root[i],root[i],1,mx,a[j].p,a[j].l),j--;
}
for(int i=1;i<=m;i++){
scanf("%lld%lld",&g,&h);
int l=1,r=mx;
int ans=-1;
while(l<=r){
int mid=(l+r)>>1;
if(ok(mid)){
ans=mid;l=mid+1;
}else{
r=mid-1;
}
}
printf("%d\n",ans);
}
return 0;
}
[2019寒假集训day3]欧铂瑞特
原题链接没找着
题解:https://blog.csdn.net/qq_34940287/article/details/87720345