【日记】12.3
12.3日记
线段树
染色问题困扰好久……
- 洛谷P2161。安排约定问题。
思路:这道题其实应该是用平衡树的。不过如果涉及到平衡树,那么就考虑能不能直接套用set。既然有只保留一个的特性,那么就可以利用set只保留一个的特性,让冲突的设计重载运算符<让其相等,即可进行去重。由于每个元素只会进出一次,因此复杂度是正确的。
注意:重载set运算符要这么干:
struct Date{
int l,r;
Date(int a=0,int b=0):l(a),r(b){}
bool operator<(const Date &x)const{
return r<x.l;
}
};
set<Date> st;
多加const。
染色解法感觉太麻烦了。
//插入区间并删除冲突+询问区间个数
#include<bits/stdc++.h>
using namespace std;
struct Date{
int l,r;
Date(int a=0,int b=0):l(a),r(b){}
bool operator<(const Date &x)const{
return r<x.l;
}
};
set<Date> st;
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i){
char s[3];
scanf("%s",s);
if (s[0]=='B')
printf("%d\n",st.size());
else{
int l,r,last=st.size();
scanf("%d%d",&l,&r);
set<Date>::iterator it=st.find(Date(l,r));
while(it!=st.end())
st.erase(it),it=st.find(Date(l,r));
printf("%d\n",last-st.size());
st.insert(Date(l,r));
}
}
return 0;
}
- POJ2777,HDU5023。线段树染色问题。
功能:区间修改+区间查询不同数个数。不同颜色个数较少。
构造:状压每个颜色。v[id]表示当前区间所有颜色(状压)。维护区间值的|值。用lazy减少修改次数保证单次logn。
注意:多组数据清空啊……
POJ2777:
//区间修改+区间不同数个数(少)
#include<cstdio>
#include<algorithm>
#define mid (l+r)/2
using namespace std;
const int M=1e5+20;
int v[4*M],lazy[4*M];
inline void push_up(int id){
v[id]=v[id*2]|v[id*2+1];
}
inline void push_down(int id){
if (lazy[id])
lazy[id*2]=lazy[id*2+1]=v[id*2]=v[id*2+1]=lazy[id],
lazy[id]=0;
}
void build(int id,int l,int r){
if (l==r){
v[id]=2;
return;
}
build(id*2,l,mid);
build(id*2+1,mid+1,r);
push_up(id);
}
void operate(int id,int l,int r,int ql,int qr,int x){
if (ql<=l&&r<=qr){
v[id]=lazy[id]=x;
return;
}
push_down(id);
if (ql<=mid)
operate(id*2,l,mid,ql,qr,x);
if (mid<qr)
operate(id*2+1,mid+1,r,ql,qr,x);
push_up(id);
}
int query(int id,int l,int r,int ql,int qr){
if (ql<=l&&r<=qr)
return v[id];
push_down(id);
int sum=0;
if (ql<=mid)
sum|=query(id*2,l,mid,ql,qr);
if (mid<qr)
sum|=query(id*2+1,mid+1,r,ql,qr);
return sum;
}
int main(){
int l,t,o;
scanf("%d%d%d",&l,&t,&o);
build(1,1,l);
for(int i=1;i<=o;++i){
char s[2];
int a,b,c;
scanf("%s",s);
if (s[0]=='C'){
scanf("%d%d%d",&a,&b,&c);
operate(1,1,l,min(a,b),max(a,b),1<<c);
}
else{
scanf("%d%d",&a,&b);
int ca=query(1,1,l,min(a,b),max(a,b)),ans=0;
while(ca)
ans+=(ca&1),ca>>=1;
printf("%d\n",ans);
}
}
return 0;
}
HDU5023:
//线段树染色升级版
#include<bits/stdc++.h>
#define mid (l+r)/2
using namespace std;
const int M=1e6+20;
int v[4*M],lazy[4*M];
inline void push_up(int id){
v[id]=v[id*2]|v[id*2+1];
}
inline void push_down(int id){
if (lazy[id])
lazy[id*2]=lazy[id*2+1]=v[id*2]=v[id*2+1]=lazy[id],
lazy[id]=0;
}
void build(int id,int l,int r){
lazy[id]=0;
if (l==r){
v[id]=(1<<2);
return;
}
build(id*2,l,mid);
build(id*2+1,mid+1,r);
push_up(id);
}
void operate(int id,int l,int r,int ql,int qr,int x){
if (ql<=l&&r<=qr){
v[id]=lazy[id]=x;
return;
}
push_down(id);
if (ql<=mid)
operate(id*2,l,mid,ql,qr,x);
if (mid<qr)
operate(id*2+1,mid+1,r,ql,qr,x);
push_up(id);
}
int query(int id,int l,int r,int ql,int qr){
if (ql<=l&&r<=qr)
return v[id];
push_down(id);
int sum=0;
if (ql<=mid)
sum|=query(id*2,l,mid,ql,qr);
if (mid<qr)
sum|=query(id*2+1,mid+1,r,ql,qr);
return sum;
}
int main(){
int l,o;
while(~scanf("%d%d",&l,&o)){
if (l==0&&o==0)
break;
build(1,1,l);
for(int i=1;i<=o;++i){
char s[2];
int a,b,c;
scanf("%s",s);
if (s[0]=='P'){
scanf("%d%d%d",&a,&b,&c);
operate(1,1,l,a,b,1<<c);
}
else{
scanf("%d%d",&a,&b);
int ca=query(1,1,l,a,b);
vector<int> v;
for(int j=1;j<=30;++j)
if((1<<j)&ca)
v.push_back(j);
printf("%d",v[0]);
for(int j=1;j<v.size();++j)
printf(" %d",v[j]);
putchar('\n');
}
}
}
return 0;
}
- HDU1166。单点加减+区间求和。
比较简单,写的很快。
//单点加减+区间求和
#include<bits/stdc++.h>
#define mid (l+r)/2
using namespace std;
const int M=5e4+20;
int v[4*M],a[M];
inline void push_up(int id){
v[id]=v[id*2]+v[id*2+1];
}
void build(int id,int l,int r){
if (l==r){
v[id]=a[l];
return;
}
build(id*2,l,mid);
build(id*2+1,mid+1,r);
push_up(id);
}
void operate(int id,int l,int r,int pos,int x){
if (l==r){
v[id]+=x;
return;
}
if (pos<=mid)
operate(id*2,l,mid,pos,x);
else
operate(id*2+1,mid+1,r,pos,x);
push_up(id);
}
int query(int id,int l,int r,int ql,int qr){
if (ql<=l&&r<=qr)
return v[id];
int ans=0;
if (ql<=mid)
ans+=query(id*2,l,mid,ql,qr);
if (mid<qr)
ans+=query(id*2+1,mid+1,r,ql,qr);
return ans;
}
int main(){
int T;
scanf("%d",&T);
for(int z=1;z<=T;++z){
printf("Case %d:\n",z);
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
build(1,1,n);
char s[10];
scanf("%s",s);
while(s[0]!='E'){
int b,c;
scanf("%d%d",&b,&c);
if (s[0]=='Q')
printf("%d\n",query(1,1,n,b,c));
else if (s[0]=='A')
operate(1,1,n,b,c);
else if (s[0]=='S')
operate(1,1,n,b,-c);
scanf("%s",s);
}
}
return 0;
}
- HDU1754。单点修改+区间查询最大值
//单点修改+区间最大值
#include<bits/stdc++.h>
#define mid (l+r)/2
using namespace std;
const int M=2e5+20;
int v[4*M],a[M];
inline void push_up(int id){
v[id]=max(v[id*2],v[id*2+1]);
}
void build(int id,int l,int r){
if (l==r){
v[id]=a[l];
return;
}
build(id*2,l,mid);
build(id*2+1,mid+1,r);
push_up(id);
}
void operate(int id,int l,int r,int pos,int x){
if (l==r){
v[id]=x;
return;
}
if (pos<=mid)
operate(id*2,l,mid,pos,x);
else
operate(id*2+1,mid+1,r,pos,x);
push_up(id);
}
int query(int id,int l,int r,int ql,int qr){
if (ql<=l&&r<=qr)
return v[id];
int ans=0;
if (ql<=mid)
ans=max(ans,query(id*2,l,mid,ql,qr));
if (mid<qr)
ans=max(ans,query(id*2+1,mid+1,r,ql,qr));
return ans;
}
int main(){
int n,m;
while(~scanf("%d%d",&n,&m)){
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
build(1,1,n);
for(int i=1;i<=m;++i){
char s[3];
scanf("%s",s);
int a,b;
scanf("%d%d",&a,&b);
if (s[0]=='Q')
printf("%d\n",query(1,1,n,a,b));
else
operate(1,1,n,a,b);
}
}
return 0;
}
- HDU1698。区间修改+区间求和。
一个是注意清空,另外一个是pushdown操作,下方的时候要乘区间长度。以后还是按照大佬的那套吧,确实会更方便一些。
//区间修改+区间和
#include<bits/stdc++.h>
#define mid (l+r)/2
using namespace std;
const int M=1e5+20;
int v[4*M],lazy[4*M];
inline void pushup(int id){
v[id]=v[id*2]+v[id*2+1];
}
inline void pushdown(int id,int l,int r){
if (lazy[id])
lazy[id*2]=lazy[id*2+1]=lazy[id],
v[id*2]=lazy[id]*(mid-l+1),
v[id*2+1]=lazy[id]*(r-mid),
lazy[id]=0;
}
void build(int id,int l,int r){
lazy[id]=0;
if (l==r){
v[id]=1;
return;
}
build(id*2,l,mid);
build(id*2+1,mid+1,r);
pushup(id);
}
void operate(int id,int l,int r,int ql,int qr,int x){
if (ql<=l&&r<=qr){
v[id]=x*(r-l+1),lazy[id]=x;
return;
}
pushdown(id,l,r);
if (ql<=mid)
operate(id*2,l,mid,ql,qr,x);
if (mid<qr)
operate(id*2+1,mid+1,r,ql,qr,x);
pushup(id);
}
int query(int id,int l,int r,int ql,int qr){
if (ql<=l&&r<=qr)
return v[id];
pushdown(id,l,r);
int sum=0;
if (ql<=mid)
sum+=query(id*2,l,mid,ql,qr);
if (mid<qr)
sum+=query(id*2+1,mid+1,r,ql,qr);
return sum;
}
int main(){
int T;
scanf("%d",&T);
for(int z=1;z<=T;++z){
int n,q;
scanf("%d%d",&n,&q);
build(1,1,n);
for(int i=1;i<=q;++i){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
operate(1,1,n,a,b,c);
}
printf("Case %d: The total value of the hook is %d.\n",z,query(1,1,n,1,n));
}
return 0;
}
- OpenJ2299。n个不同数组成的数列,问有多少个逆序对。
模板:一维偏序。可以归并,也可以树状数组。首先离散化,之后从后往前,将a[i]置为1,将1-i-1的区间和加入答案即可。注意开LL!
//单点修改+区间查询和
#include<bits/stdc++.h>
using namespace std;
const int M=5e5+20;
int num_bit,a[M],b[M],c[M];
unordered_map<int,int> rev;
inline int lowbit(int x){return x&(-x);}
inline void update(int x,int k){
while(x<=num_bit)
c[x]+=k,x+=lowbit(x);
}
inline int query(int x){//1-x的和
int ans=0;
while(x)
ans+=c[x],x-=lowbit(x);
return ans;
}
int main(){
int n;
scanf("%d",&n);
while(n){
num_bit=n;
for (int i=1;i<=n;++i)
scanf("%d",&a[i]),b[i]=a[i],c[i]=0;
rev.clear();
sort(a+1,a+n+1);
for(int i=1;i<=n;++i)
rev[a[i]]=i;
long long ans=0;
for(int i=n;i>=1;--i)
ans+=query(rev[b[i]]),update(rev[b[i]],1);
printf("%lld\n",ans);
scanf("%d",&n);
}
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步