做题小记·数据结构
并查集
P107. 亲戚
板,写的路径压缩
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=20005,M=1000005;
int fa[N];
int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
}
void unionn(int x,int y){
fa[find(y)]=find(x);
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=m;i++){
int x,y;
scanf("%d%d",&x,&y);
unionn(x,y);
}
int q;
scanf("%d",&q);
while(q--){
int x,y;
scanf("%d%d",&x,&y);
if(find(x)==find(y)){
printf("Yes\n");
}else{
printf("No\n");
}
}
return 0;
}
P197. 【Usaco2004 Open】Cube Stacking
假蓝题
这个是带权并查集+路径压缩,就是维护的时候有点麻烦
\(fa[i]\) 表示 \(i\) 所在集合最下面的方块,\(h[i]\) 表示 \(i\) 前面有几个方块
因为我们每回只改变了集合根节点的 \(h\),所以在查询子节点的时候要把上面的传下来,啊好像类似于线段树里面的某个操作。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=30005;
int fa[N],size[N],h[N];
int find(int x){
if(fa[x]==x)return x;
int father=find(fa[x]);
h[x]+=h[fa[x]];
return fa[x]=father;
}
void unionn(int x,int y){
int fx=find(x),fy=find(y);
fa[fx]=fy;
h[fx]+=size[fy];
size[fy]+=size[fx];
}
int main(){
for(int i=1;i<=30000;i++){fa[i]=i;size[i]=1;}
int p;
scanf("%d",&p);
while(p--){
char op;
int x,y;
cin >> op;
if(op=='M'){
scanf("%d%d",&x,&y);
unionn(x,y);
}else{
scanf("%d",&x);
find(x);
printf("%d\n",h[x]);
}
}
return 0;
}
P108. 银河英雄传说
和上面是一样的,答案是 \(abs(h[x]-h[y])-1\)
但是我为啥 T 俩点。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=30005;
int fa[N],size[N],h[N];
int find(int x){
if(fa[x]==x)return x;
int father=find(fa[x]);
h[x]+=h[fa[x]];
return fa[x]=father;
}
void unionn(int x,int y){
int fx=find(x),fy=find(y);
fa[fx]=fy;
h[fx]+=size[fy];
size[fy]+=size[fx];
}
int main(){
for(int i=1;i<=30000;i++){fa[i]=i;size[i]=1;}
int p;
scanf("%d",&p);
while(p--){
char op;
int x,y;
cin >> op;
scanf("%d%d",&x,&y);
if(op=='M'){
unionn(x,y);
}else{
int fx=find(x),fy=find(y);
fx==fy?printf("%d\n",abs(h[x]-h[y])-1):printf("-1\n");
}
}
return 0;
}
P110. 食物链
开三个并查集,表示三个类群 \(ABC\) 。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=50005;
int fa[3*N];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void unionn(int x,int y){int fx=find(x),fy=find(y);fa[fx]=fy;}
int main(){
int n,k;scanf("%d%d",&n,&k);
for(int i=1;i<=3*n;i++)fa[i]=i;
int ans=0;
while(k--){
int op,x,y;scanf("%d%d%d",&op,&x,&y);
if(x>n||y>n){ans++;continue;}
if(op==1){
if(find(x+n)==find(y)||find(y+n)==find(x)){ans++;continue;}
unionn(x,y);
unionn(x+n,y+n);
unionn(x+n+n,y+n+n);
}else{
if(find(x)==find(y)||find(x+n)==find(y)){ans++;continue;}
unionn(x,y+n);
unionn(x+n,y+n+n);
unionn(x+n+n,y);
}
}
printf("%d\n",ans);
return 0;
}
P1955 [NOI2015] 程序自动分析
先连相等的,然后用不相等的来验证可不可行,就是判断不相等的在不在一个集合里。
\(i,j\) 太大了要离散化
告诉你个秘密 不离散化有90分
点击查看代码
#include <bits/stdc++.h>
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
const int N=1000005,M=1005;
int fa[3*N],lsh[3*N],b[3*N];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void unionn(int x,int y){int fx=find(x),fy=find(y);fa[fx]=fy;}
struct node{
int x,y,op;
}a[N];
bool cmp(node a,node b){return a.op>b.op;}
int cnt3=0;
int query(int x){
int l=1,r=cnt3,mid,res;
while(l<=r){
mid=(l+r)/2;
if(lsh[mid]<=x){
res=mid;
l=mid+1;
}else{
r=mid-1;
}
}
return res;
}
int main(){
int t;t=read();
while(t--){
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(lsh,0,sizeof(lsh));
for(int i=1;i<=3*N;i++)fa[i]=i;
int n;n=read();
int cnt=0,cnt2=0;
while(n--){
int x,y,op;x=read();y=read();op=read();
a[++cnt]={x,y,op};
b[++cnt2]=x,b[++cnt2]=y;
}
sort(b+1,b+cnt2+1);
for(int i=1;i<=cnt2;i++){
if(b[i]!=b[i-1]){
lsh[++cnt3]=b[i];
}
}
sort(a+1,a+cnt+1,cmp);
int flag=0;
for(int i=1;i<=cnt;i++){
if(a[i].op==1){
unionn(query(a[i].x),query(a[i].y));
}else{
if(find(query(a[i].x))==find(query(a[i].y))){flag=1;printf("NO\n");break;}
}
}
if(!flag)printf("YES\n");
}
return 0;
}
树状数组
P111. 求和
板板,单点修改区间查询。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
const int N=100005;
int n,c[N],a[N];
int lowbit(int x){return x&(-x);}
void add(int x,int k){
while(x<=n){
c[x]+=k;
x+=lowbit(x);
}
}
int query(int x){
int ans=0;
while(x){
ans+=c[x];
x-=lowbit(x);
}
return ans;
}
int main(){
n=read();
for(int i=1;i<=n;i++){
a[i]=read();
add(i,a[i]);
}
int m;m=read();
for(int i=1;i<=m;i++){
char op;cin >> op;
int x,y;x=read();y=read();
if(op=='Q'){
printf("%d\n",query(y)-query(x-1));
}else{
add(x,y-a[x]);
a[x]=y;
}
}
return 0;
}
P112. 星星点灯
\(y\) 已经是从小到大给出的所以直接不考虑,维护 \(x\) 的前缀和
因为 \(x\) 可能等于0,\(while(x<=N)\) 时会死循环,所以放 \(x+1\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
const int N=32005;
int n,c[N];
int lowbit(int x){return x&(-x);}
void add(int x){while(x<=N){c[x]++;x+=lowbit(x);}}
int query(int x){int ans=0;while(x){ans+=c[x];x-=lowbit(x);}return ans;}
int main(){
n=read();
for(int i=1;i<=n;i++){
int x,y;x=read();y=read();
printf("%d\n",query(x+1));
add(x+1);
}
return 0;
}
线段树
P275. 敌兵布阵
单点修改区间查询的板
点击查看代码
#include <bits/stdc++.h>
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
const int N=50005;
struct node{
int l,r,sum;
}tr[4*N];
#define lson(x) (x<<1)
#define rson(x) ((x<<1)|1)
void pushup(int i){
tr[i].sum=tr[lson(i)].sum+tr[rson(i)].sum;
}
void build(int l,int r,int i){
tr[i].l=l,tr[i].r=r;
if(tr[i].l==tr[i].r){
tr[i].sum=read();
return;
}
int mid=(l+r)>>1;
build(l,mid,lson(i));
build(mid+1,r,rson(i));
pushup(i);
}
void update(int i,int dis,int k){
if(tr[i].l==tr[i].r){
tr[i].sum+=k;
return;
}
if(dis<=tr[lson(i)].r){
update(lson(i),dis,k);
}else{
update(rson(i),dis,k);
}
pushup(i);
}
int query(int i,int l,int r){
if(l<=tr[i].l&&tr[i].r<=r)return tr[i].sum;
int ans=0;
if(l<=tr[lson(i)].r)ans+=query(lson(i),l,r);
if(r>=tr[rson(i)].l)ans+=query(rson(i),l,r);
return ans;
}
int main(){
int n;n=read();
build(1,n,1);
char c[8];
while(cin >> c){
if(c[0]=='A'){
int x,y;x=read();y=read();
update(1,x,y);
}else if(c[0]=='S'){
int x,y;x=read();y=read();
update(1,x,-y);
}else if(c[0]=='Q'){
int x,y;x=read();y=read();
printf("%d\n",query(1,x,y));
}else{
break;
}
}
return 0;
}
P3368 【模板】树状数组 2
区间修改单点查询的板
注意一下要返回值的函数是否都返回值了,要不然开O2会RE
哦这种叫 未定义行为 ,在本地编译器中加入 -Wall 指令可以检查
点击查看代码
#include <bits/stdc++.h>
#define lson(x) (x<<1)
#define rson(x) ((x<<1)|1)
using namespace std;
const int N=500005;
struct node{
int l,r,sum;
}tr[4*N];
void pushup(int i){tr[i].sum=tr[lson(i)].sum+tr[rson(i)].sum;}
void build(int l,int r,int i){
tr[i].l=l,tr[i].r=r;
if(tr[i].l==tr[i].r){scanf("%d",&tr[i].sum);return;}
int mid=(l+r)>>1;
build(l,mid,lson(i));
build(mid+1,r,rson(i));
}
void update(int i,int l,int r,int k){
if(l<=tr[i].l&&tr[i].r<=r){tr[i].sum+=k;return;}
if(l<=tr[lson(i)].r)update(lson(i),l,r,k);
if(r>=tr[rson(i)].l)update(rson(i),l,r,k);
}
int ans=0;
void query(int i,int dis){
ans+=tr[i].sum;
if(tr[i].l==tr[i].r)return;
if(dis<=tr[lson(i)].r){query(lson(i),dis);}else{query(rson(i),dis);}
}
int main(){
int n,m;scanf("%d%d",&n,&m);
build(1,n,1);
for(int i=1;i<=m;i++){
int op;scanf("%d",&op);
if(op==1){
int x,y,k;scanf("%d%d%d",&x,&y,&k);
update(1,x,y,k);
}else{
ans=0;
int x;scanf("%d",&x);
query(1,x);
printf("%d\n",ans);
}
}
return 0;
}
P3372 【模板】线段树 1
区间修改区间查询加法
点击查看代码
#include <bits/stdc++.h>
#define int long long
#define lson(x) (x<<1)
#define rson(x) ((x<<1)|1)
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
const int N=1e5+5;
struct Seg_tree{
int l,r,sum,lz;
}tr[4*N];
void pushup(int i){tr[i].sum=tr[lson(i)].sum+tr[rson(i)].sum;}
void pushdown(int i){
tr[lson(i)].lz+=tr[i].lz;tr[rson(i)].lz+=tr[i].lz;
tr[lson(i)].sum+=(tr[lson(i)].r-tr[lson(i)].l+1)*tr[i].lz;
tr[rson(i)].sum+=(tr[rson(i)].r-tr[rson(i)].l+1)*tr[i].lz;
tr[i].lz=0;
}
void build(int l,int r,int i){
tr[i].l=l,tr[i].r=r;
if(tr[i].l==tr[i].r){tr[i].sum=read();return;}
int mid=(l+r)>>1;
build(l,mid,lson(i));
build(mid+1,r,rson(i));
pushup(i);
}
void update(int i,int l,int r,int k){
if(l<=tr[i].l&&tr[i].r<=r){tr[i].sum+=(tr[i].r-tr[i].l+1)*k;tr[i].lz+=k;return;}
pushdown(i);
if(l<=tr[lson(i)].r){update(lson(i),l,r,k);}
if(r>=tr[rson(i)].l){update(rson(i),l,r,k);}
pushup(i);
}
int query(int i,int l,int r){
if(l<=tr[i].l&&tr[i].r<=r){return tr[i].sum;}
pushdown(i);
int cal=0;
if(l<=tr[lson(i)].r){cal+=query(lson(i),l,r);}
if(r>=tr[rson(i)].l){cal+=query(rson(i),l,r);}
return cal;
}
signed main(){
int n,m;n=read();m=read();
build(1,n,1);
for(int i=1;i<=m;i++){
int op;op=read();
int x,y,k;x=read();y=read();
if(op==1){k=read();update(1,x,y,k);
}else{printf("%lld\n",query(1,x,y));}
}
return 0;
}
P281. 【Ahoi2009】维护序列
区间既有乘也有加
点击查看代码
#include <bits/stdc++.h>
#define int long long
#define lson(x) (x<<1)
#define rson(x) (x<<1|1)
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
const int N=1e5+5;
int m;
struct Seg_tree{
int l,r,sum,plz,mlz=1;
}tr[4*N];
void pushup(int i){tr[i].sum=(tr[lson(i)].sum+tr[rson(i)].sum)%m;}
void pushdown(int i){
tr[lson(i)].mlz=(tr[i].mlz*tr[lson(i)].mlz)%m;
tr[rson(i)].mlz=(tr[i].mlz*tr[rson(i)].mlz)%m;
tr[lson(i)].plz=(tr[lson(i)].plz*tr[i].mlz+tr[i].plz)%m;
tr[rson(i)].plz=(tr[rson(i)].plz*tr[i].mlz+tr[i].plz)%m;
tr[lson(i)].sum=((tr[lson(i)].sum*tr[i].mlz)%m+((tr[lson(i)].r-tr[lson(i)].l+1)*tr[i].plz)%m)%m;
tr[rson(i)].sum=((tr[rson(i)].sum*tr[i].mlz)%m+((tr[rson(i)].r-tr[rson(i)].l+1)*tr[i].plz)%m)%m;
tr[i].mlz=1;tr[i].plz=0;
}
void build(int l,int r,int i){
tr[i].l=l,tr[i].r=r;
if(tr[i].l==tr[i].r){tr[i].mlz=1;tr[i].sum=read();return;}
int mid=(l+r)>>1;
build(l,mid,lson(i));
build(mid+1,r,rson(i));
pushup(i);
}
void mul(int i,int l,int r,int k){
if(l<=tr[i].l&&tr[i].r<=r){tr[i].sum=(tr[i].sum*k)%m;tr[i].mlz=(tr[i].mlz*k)%m;tr[i].plz=(tr[i].plz*k)%m;return;}
pushdown(i);
if(l<=tr[lson(i)].r){mul(lson(i),l,r,k);}
if(r>=tr[rson(i)].l){mul(rson(i),l,r,k);}
pushup(i);
}
void add(int i,int l,int r,int k){
if(l<=tr[i].l&&tr[i].r<=r){tr[i].plz=(tr[i].plz+k)%m;tr[i].sum=(tr[i].sum+((tr[i].r-tr[i].l+1)*k)%m)%m;return;}
pushdown(i);
if(l<=tr[lson(i)].r){add(lson(i),l,r,k);}
if(r>=tr[rson(i)].l){add(rson(i),l,r,k);}
pushup(i);
}
int query(int i,int l,int r){
if(l<=tr[i].l&&tr[i].r<=r){return tr[i].sum;}
pushdown(i);
int cal=0;
if(l<=tr[lson(i)].r){cal=(cal+query(lson(i),l,r))%m;}
if(r>=tr[rson(i)].l){cal=(cal+query(rson(i),l,r))%m;}
return cal;
}
signed main(){
int n,q;n=read();m=read();
build(1,n,1);
q=read();
while(q--){
int op,x,y,k;op=read();x=read();y=read();
if(op==1){k=read();mul(1,x,y,k);
}else if(op==2){k=read();add(1,x,y,k);
}else{printf("%lld\n",query(1,x,y)%m);}
}
return 0;
}
P282. 借教室续
平均数维护区间和再除以数量就行,方差公式可以推导一下,然后开两个线段树分别维护区间和和区间平方和就行。公式不想写了。
就是要注意一下更改时先改平方和再改区间和,因为改平方和时会用到更改前的区间和。
点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
#define lson(x) (x<<1)
#define rson(x) (x<<1|1)
const int N=1e5+5;
struct Seg_tree{
int l,r,lz,sum;
}tr1[4*N],tr2[4*N];
int a[4*N];
void pushup(int i){
tr2[i].sum=tr2[lson(i)].sum+tr2[rson(i)].sum;
tr1[i].sum=tr1[lson(i)].sum+tr1[rson(i)].sum;
}
void pushdown(int i){
tr1[lson(i)].lz+=tr1[i].lz;tr1[rson(i)].lz+=tr1[i].lz;
tr2[lson(i)].lz+=tr2[i].lz;tr2[rson(i)].lz+=tr2[i].lz;
tr2[lson(i)].sum+=(tr2[lson(i)].r-tr2[lson(i)].l+1)*tr2[i].lz*tr2[i].lz+2*tr2[i].lz*tr1[lson(i)].sum;
tr2[rson(i)].sum+=(tr2[rson(i)].r-tr2[rson(i)].l+1)*tr2[i].lz*tr2[i].lz+2*tr2[i].lz*tr1[rson(i)].sum;
tr1[lson(i)].sum+=tr1[i].lz*(tr1[lson(i)].r-tr1[lson(i)].l+1);
tr1[rson(i)].sum+=tr1[i].lz*(tr1[rson(i)].r-tr1[rson(i)].l+1);
tr1[i].lz=0;
tr2[i].lz=0;
}
void add(int i,int l,int r,int k){
if(l<=tr2[i].l&&tr2[i].r<=r){
tr2[i].lz+=k;tr2[i].sum=tr2[i].sum+k*k*(tr2[i].r-tr2[i].l+1)+2*k*tr1[i].sum;
tr1[i].lz+=k;tr1[i].sum+=(tr1[i].r-tr1[i].l+1)*k;
return;
}
pushdown(i);
if(l<=tr2[lson(i)].r){add(lson(i),l,r,k);}
if(r>=tr2[rson(i)].l){add(rson(i),l,r,k);}
pushup(i);
}
void build(int l,int r,int i){
tr1[i].l=l,tr1[i].r=r;
tr2[i].l=l,tr2[i].r=r;
if(tr1[i].l==tr1[i].r){tr1[i].sum=read();tr2[i].sum=tr1[i].sum*tr1[i].sum;return;}
int mid=(l+r)>>1;
build(l,mid,lson(i));
build(mid+1,r,rson(i));
pushup(i);
}
int query1(int i,int l,int r){
if(l<=tr1[i].l&&tr1[i].r<=r){return tr1[i].sum;}
pushdown(i);
int cal=0;
if(l<=tr1[lson(i)].r){cal+=query1(lson(i),l,r);}
if(r>=tr1[rson(i)].l){cal+=query1(rson(i),l,r);}
return cal;
}
int query2(int i,int l,int r){
if(l<=tr2[i].l&&tr2[i].r<=r){return tr2[i].sum;}
pushdown(i);
int cal=0;
if(l<=tr2[lson(i)].r){cal+=query2(lson(i),l,r);}
if(r>=tr2[rson(i)].l){cal+=query2(rson(i),l,r);}
return cal;
}
int gcd(int a,int b){return b==0?a:gcd(b,a%b);}
signed main(){
int n,m;n=read();m=read();
build(1,n,1);
for(int i=1;i<=m;i++){
int op,x,y;op=read();x=read();y=read();
if(op==1){
int d;d=read();
add(1,x,y,d);
}else if(op==2){
int a=query1(1,x,y),b=y-x+1;
printf("%lld/%lld\n",a/gcd(a,b),b/gcd(a,b));
}else{
int cnt=y-x+1;
int a=query2(1,x,y)*cnt-query1(1,x,y)*query1(1,x,y);
int b=cnt*cnt;
printf("%lld/%lld\n",a/gcd(a,b),b/gcd(a,b));
}
}
return 0;
}
P121. 小白逛公园
\(tr[i].la\) 为包含左端点的最大值,\(tr[i].ra\) 为包含有段点的最大值。合并的时候画个图好理解。
比较坑的一点是题目里没说 \(a<b\) 所以 \(a>b\) 时要交换一下。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int N=5e5+5;
struct Seg_tree{
int l,r,la,ra,sum,ans;
}tr[4*N];
#define lson(x) (x<<1)
#define rson(x) (x<<1|1)
void pushup(int i){
tr[i].sum=tr[lson(i)].sum+tr[rson(i)].sum;
tr[i].la=max(tr[lson(i)].la,tr[lson(i)].sum+tr[rson(i)].la);
tr[i].ra=max(tr[rson(i)].ra,tr[rson(i)].sum+tr[lson(i)].ra);
tr[i].ans=max(max(tr[lson(i)].ans,tr[rson(i)].ans),tr[lson(i)].ra+tr[rson(i)].la);
}
void build(int l,int r,int i){
tr[i].l=l,tr[i].r=r;
if(tr[i].l==tr[i].r){scanf("%d",&tr[i].sum);tr[i].la=tr[i].ra=tr[i].ans=tr[i].sum;return;}
int mid=(l+r)>>1;
build(l,mid,lson(i));
build(mid+1,r,rson(i));
pushup(i);
}
void update(int i,int dis,int k){
if(tr[i].l==tr[i].r){tr[i].sum=tr[i].ans=tr[i].la=tr[i].ra=k;return;}
if(dis<=tr[lson(i)].r){update(lson(i),dis,k);
}else{update(rson(i),dis,k);}
pushup(i);
}
Seg_tree query(int i,int l,int r){
if(l<=tr[i].l&&tr[i].r<=r){return tr[i];}
if(r<=tr[lson(i)].r){return query(lson(i),l,r);
}else if(l>=tr[rson(i)].l){return query(rson(i),l,r);
}else{
Seg_tree a,b,c;
a=query(lson(i),l,r);b=query(rson(i),l,r);
c.la=max(a.la,a.sum+b.la);c.ra=max(b.ra,b.sum+a.ra);
c.ans=max(max(a.ans,b.ans),a.ra+b.la);
return c;
}
}
int main(){
int n,m;scanf("%d%d",&n,&m);
build(1,n,1);
while(m--){
int op,x,y;scanf("%d%d%d",&op,&x,&y);
if(op==1){if(x>y)swap(x,y);printf("%d\n",query(1,x,y).ans);
}else{update(1,x,y);}
}
return 0;
}
P336. 【USACO 2008 FEB】Hotel
感觉和上面那个差不多,\(tr[i].la\) 为包含左端点的最大连续为空的长度, \(tr[i].ra\) 同理。修改传 \(lazytag\) 的时候区分一下是入住还是退房。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
const int N=5e4+5;
struct Seg_tree{
int l,r,la,ra,ans,lz;
}tr[4*N];
#define lson(x) (x<<1)
#define rson(x) (x<<1|1)
void pushup(int i){
if(tr[lson(i)].ans==tr[lson(i)].r-tr[lson(i)].l+1){
tr[i].la=tr[lson(i)].r-tr[lson(i)].l+1+tr[rson(i)].la;
}else{tr[i].la=tr[lson(i)].la;}
if(tr[rson(i)].ans==tr[rson(i)].r-tr[rson(i)].l+1){
tr[i].ra=tr[rson(i)].r-tr[rson(i)].l+1+tr[lson(i)].ra;
}else{tr[i].ra=tr[rson(i)].ra;}
tr[i].ans=max(max(tr[lson(i)].ans,tr[rson(i)].ans),tr[lson(i)].ra+tr[rson(i)].la);
}
void build(int l,int r,int i){
tr[i].l=l;tr[i].r=r;
if(tr[i].l==tr[i].r){tr[i].la=tr[i].ra=tr[i].ans=1;return;}
int mid=(l+r)>>1;
build(l,mid,lson(i));
build(mid+1,r,rson(i));
pushup(i);
}
void pushdown(int i){
if(tr[i].lz==0)return;
tr[lson(i)].lz=tr[i].lz;tr[rson(i)].lz=tr[i].lz;
if(tr[i].lz==1){
tr[lson(i)].ans=tr[lson(i)].la=tr[lson(i)].ra=tr[lson(i)].r-tr[lson(i)].l+1;
tr[rson(i)].ans=tr[rson(i)].la=tr[rson(i)].ra=tr[rson(i)].r-tr[rson(i)].l+1;
}else{
tr[lson(i)].ans=tr[lson(i)].la=tr[lson(i)].ra=0;
tr[rson(i)].ans=tr[rson(i)].la=tr[rson(i)].ra=0;
}
tr[i].lz=0;
}
void update(int i,int l,int r,int k){
if(l<=tr[i].l&&tr[i].r<=r){
if(k==1){
tr[i].lz=1;tr[i].ans=tr[i].la=tr[i].ra=tr[i].r-tr[i].l+1;
return;
}else{
tr[i].lz=2;tr[i].ans=tr[i].la=tr[i].ra=0;
return;
}
}
pushdown(i);
if(l<=tr[lson(i)].r){update(lson(i),l,r,k);}
if(r>=tr[rson(i)].l){update(rson(i),l,r,k);}
pushup(i);
}
int query(int i,int l,int r,int x){
pushdown(i);
if(tr[lson(i)].ans>=x){
return query(lson(i),l,r,x);
}else if(tr[lson(i)].ra+tr[rson(i)].la>=x){
return tr[lson(i)].r-tr[lson(i)].ra+1;
}else{
return query(rson(i),l,r,x);
}
}
int main(){
int n,m;n=read();m=read();
build(1,n,1);
while(m--){
int op,x;op=read();x=read();
if(op==1){
if(tr[1].ans>=x){
int l=query(1,1,n,x);
printf("%d\n",l);
update(1,l,l+x-1,2);
}else{printf("0\n");}
}else{
int y;y=read();
update(1,x,x+y-1,1);
}
}
return 0;
}
P4145 上帝造题的七分钟 2 / 花神游历各国
直接单点修改的话复杂度 \(O(n^2logn)\) ,好像过不去捏。
然后发现 \(10^{12}\) 开 \(6\) 次根向下取整就变成了 \(1\) ,于是乎我们可以维护区间最大值,如果最大值小于等于 \(1\) 就跳过,复杂度降为 \(O(nlogn)\) 忽略常数。
点击查看代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
const int N=1e5+5;
struct Seg_tree{
int l,r,sum,maxx;
}tr[4*N];
#define lson(x) (x<<1)
#define rson(x) (x<<1|1)
void pushup(int i){
tr[i].sum=tr[lson(i)].sum+tr[rson(i)].sum;
tr[i].maxx=max(tr[lson(i)].maxx,tr[rson(i)].maxx);
}
void build(int l,int r,int i){
tr[i].l=l,tr[i].r=r;
if(tr[i].l==tr[i].r){tr[i].maxx=tr[i].sum=read();return;}
int mid=(l+r)>>1;
build(l,mid,lson(i));build(mid+1,r,rson(i));
pushup(i);
}
void update(int i,int l,int r){
if(tr[i].l==tr[i].r){tr[i].sum=sqrt(tr[i].sum);tr[i].maxx=tr[i].sum;return;}
if(l<=tr[lson(i)].r&&tr[lson(i)].maxx>1){update(lson(i),l,r);}
if(r>=tr[rson(i)].l&&tr[rson(i)].maxx>1){update(rson(i),l,r);}
pushup(i);
}
int query(int i,int l,int r){
if(l<=tr[i].l&&tr[i].r<=r){return tr[i].sum;}
int cal=0;
if(l<=tr[lson(i)].r){cal+=query(lson(i),l,r);}
if(r>=tr[rson(i)].l){cal+=query(rson(i),l,r);}
return cal;
}
signed main(){
int n;n=read();
build(1,n,1);
int m;m=read();
while(m--){
int k,x,y;k=read();x=read();y=read();
if(x>y)swap(x,y);
if(!k){
update(1,x,y);
}else{
printf("%lld\n",query(1,x,y));
}
}
return 0;
}
P5490 【模板】扫描线
线段树的一种应用,来学一下。
点击查看代码
#include <bits/stdc++.h>
#define int long long
#define lson(x) (x<<1)
#define rson(x) (x<<1|1)
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
const int N=1e5+5;
struct Seg_tree{
int l,r,sum,cover;
}tr[8*N];
struct scanline{
int l,r,h,mark;
friend bool operator < (scanline x,scanline y){
return x.h<y.h;
}
}a[2*N];
int x[2*N],cnt;
void pushup(int i){
if(tr[i].l==tr[i].r){
if(tr[i].cover){tr[i].sum=x[tr[i].r+1]-x[tr[i].l];
}else{tr[i].sum=0;}
}else{
if(tr[i].cover){tr[i].sum=x[tr[i].r+1]-x[tr[i].l];
}else{tr[i].sum=tr[lson(i)].sum+tr[rson(i)].sum;}
}
}
void build(int l,int r,int i){
tr[i].l=l,tr[i].r=r;
if(l==r){return;}
int mid=(l+r)>>1;
build(l,mid,lson(i));build(mid+1,r,rson(i));
}
void update(int i,int l,int r,int k){
if(l<=x[tr[i].l]&&x[tr[i].r+1]<=r){tr[i].cover+=k;pushup(i);return;}
if(l<x[tr[lson(i)].r+1]){update(lson(i),l,r,k);}
if(r>x[tr[rson(i)].l]){update(rson(i),l,r,k);}
pushup(i);
}
signed main(){
int n;n=read();
int x1,y1,x2,y2;
for(int i=1;i<=n;i++){
x1=read();y1=read();x2=read();y2=read();
a[++cnt]=(scanline){x1,x2,y1,1};
x[cnt]=x1;
a[++cnt]=(scanline){x1,x2,y2,-1};
x[cnt]=x2;
}
sort(x+1,x+cnt+1);
sort(a+1,a+cnt+1);
int ans=0;
build(1,cnt-1,1);
for(int i=1;i<cnt;i++){
update(1,a[i].l,a[i].r,a[i].mark);
ans+=tr[1].sum*(a[i+1].h-a[i].h);
}
printf("%lld\n",ans);
return 0;
}
P283. Picture
扫描线周长并捏
点击查看代码
#include <bits/stdc++.h>
#define int long long
#define lson(x) (x<<1)
#define rson(x) (x<<1|1)
using namespace std;
const int N=5e3+5;
struct scanline{
int l,r,h,mark;
friend bool operator < (scanline x,scanline y){
return (x.h==y.h)?(x.mark>y.mark):(x.h<y.h);
}
}a[2*N];
struct Seg_tree{
int l,r,sum,num,len,lp,rp;
}tr[8*N];
int cnt,x[2*N];
void build(int l,int r,int i){
tr[i].l=l,tr[i].r=r;
if(l==r){return;}
int mid=(l+r)>>1;
build(l,mid,lson(i));
build(mid+1,r,rson(i));
}
void pushup(int i){
if(tr[i].l==tr[i].r){
if(tr[i].sum){tr[i].lp=tr[i].rp=1;tr[i].num=2;tr[i].len=x[tr[i].r+1]-x[tr[i].l];
}else{tr[i].lp=tr[i].rp=0;tr[i].num=0;tr[i].len=0;}
}else{
if(tr[i].sum){
tr[i].lp=tr[i].rp=1;tr[i].num=2;tr[i].len=x[tr[i].r+1]-x[tr[i].l];
}else{
tr[i].lp=tr[lson(i)].lp;tr[i].rp=tr[rson(i)].rp;
tr[i].len=tr[lson(i)].len+tr[rson(i)].len;
tr[i].num=tr[lson(i)].num+tr[rson(i)].num;
if(tr[lson(i)].rp&&tr[rson(i)].lp)tr[i].num-=2;
}
}
}
void update(int i,int l,int r,int k){
if(l<=x[tr[i].l]&&x[tr[i].r+1]<=r){tr[i].sum+=k;pushup(i);return;}
if(l<x[tr[lson(i)].r+1])update(lson(i),l,r,k);
if(r>x[tr[rson(i)].l])update(rson(i),l,r,k);
pushup(i);
}
signed main(){
int n;scanf("%lld",&n);
int x1,y1,x2,y2;
for(int i=1;i<=n;i++){
scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2);
a[++cnt]=(scanline){x1,x2,y1,1};x[cnt]=x1;
a[++cnt]=(scanline){x1,x2,y2,-1};x[cnt]=x2;
}
sort(x+1,x+cnt+1);
sort(a+1,a+cnt+1);
int len=unique(x+1,x+cnt+1)-x-1;
build(1,len-1,1);
int ans=0,last=0;
for(int i=1;i<=cnt;i++){
update(1,a[i].l,a[i].r,a[i].mark);
ans+=abs(tr[1].len-last);last=tr[1].len;
ans+=tr[1].num*(a[i+1].h-a[i].h);
}
printf("%lld\n",ans);
return 0;
}