POJ - 2528 - Mayor's posters
题目链接<http://poj.org/problem?id=2528>
题意:
在范围为1e9的瓷砖上,依次覆盖1e4张海报,每张海报各不相同,问最后能看到的海报有多少张。
题解:
离散加区间修改和最后一次的区间查询。
离散有两种考虑方式:
- 离散边界,也就是说对于瓷砖区间r+1或者l-1。
- 离散区间,这样容易忽略离散后的顺序相邻但实际的位置并不相邻的情况,可以考虑在每个位置不相邻的节点中间另外加一个节点即可。
#include<cstdio>
#include<algorithm>
#include<string.h>
using namespace std;
typedef long long ll;
const int N=1e6+7;
int lazy[N<<2];
int x[N],y[N];
int a[N*2];
bool vis[N*2];
int ans=0;
void pd(int rt){
if(lazy[rt]!=-1){
lazy[rt<<1]=lazy[rt<<1|1]=lazy[rt];
lazy[rt]=-1;
}
}
void update(int L,int R,int l,int r,int rt,int tp){
if(L<=l&&r<=R){
lazy[rt]=tp;
return;
}
pd(rt);
int m=l+r>>1;
if(L<=m) update(L,R,l,m,rt<<1,tp);
if(m<R) update(L,R,m+1,r,rt<<1|1,tp);
}
void query(int l,int r,int rt){
if(lazy[rt]!=-1){
if(!vis[lazy[rt]]) ans++;
vis[lazy[rt]]=true;
return;
}
if(l==r)return;
int m=l+r>>1;
query(l,m,rt<<1);
query(m+1,r,rt<<1|1);
}
int main()
{
int t,n;
scanf("%d",&t);
while(t--){
memset(vis,false,sizeof(vis));
memset(lazy,-1,sizeof(lazy));
scanf("%d",&n);
ans=0;
int tot=0;
for(int i=1;i<=n;i++){
scanf("%d%d",&x[i],&y[i]);
y[i]+=1;
a[++tot]=x[i];
a[++tot]=y[i];
}
sort(a+1,a+1+tot);
tot=unique(a+1,a+1+tot)-a-1;
/*
int tt=tot;
for(int i=2;i<=tot;i++){
if(a[i]>a[i-1]+1) a[++tt]=a[i-1]+1;
}
tot=tt;
sort(a+1,a+1+tot);
for(int i=1;i<=n;i++){
x[i]=lower_bound(a+1,a+1+tot,x[i])-a;
y[i]=lower_bound(a+1,a+1+tot,y[i])-a;
update(x[i],y[i],1,tot,1,i);
}
*/
for(int i=1;i<=n;i++){
x[i]=lower_bound(a+1,a+1+tot,x[i])-a;
y[i]=lower_bound(a+1,a+1+tot,y[i])-a;
update(x[i],y[i]-1,1,tot,1,i);
}
query(1,tot,1);
printf("%d\n",ans);
}
}
HDU - 4027 - Can you answer these queries?
题目链接<http://acm.hdu.edu.cn/showproblem.php?pid=4027>
题意:
区间修改:区间内的每个数变成原来数字的平方根。
区间查询:查询区间和。
数字的范围小于。
题解:
对于范围在内的,最多做7此平方根。所以线段树每个节点最多只需要做7次的修改。记录每个区间的修改次数即可。
#include<cstdio>
#include<algorithm>
#include<string.h>
#include<cmath>
using namespace std;
typedef unsigned long long ll;
const int N=1e5+7;
ll sum[N<<2];
int num[N<<2];
void pu(int rt){
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt){
if(l==r){
scanf("%llu",&sum[rt]);
return;
}
int m=l+r>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
pu(rt);
}
void update(int L,int R,int l,int r,int rt){
if(L<=l&&r<=R){
if(num[rt]>=7){
sum[rt]=r-l+1;
return;
}
num[rt]++;
}
if(l==r){sum[rt]=(ll)sqrt(sum[rt]);return;}
int m=r+l>>1;
if(L<=m) update(L,R,l,m,rt<<1);
if(m<R) update(L,R,m+1,r,rt<<1|1);
pu(rt);
}
ll query(int L,int R,int l,int r,int rt){
if(L<=l&&r<=R) return sum[rt];
int m=r+l>>1;
ll res=0;
if(L<=m) res+=query(L,R,l,m,rt<<1);
if(m<R) res+=query(L,R,m+1,r,rt<<1|1);
return res;
}
int main()
{
int n,q;
int cs=0;
while(scanf("%d",&n)!=EOF){
printf("Case #%d:\n",++cs);
build(1,n,1);
memset(num,0,sizeof(num));
scanf("%d",&q);
int a,b,c;
while(q--){
scanf("%d%d%d",&a,&b,&c);
if(b>c) swap(b,c);
if(a==0) update(b,c,1,n,1);
else printf("%llu\n",query(b,c,1,n,1));
}
printf("\n");
}
}
HDU - 1540 - Tunnel Warfare
题目链接<http://acm.hdu.edu.cn/showproblem.php?pid=1540>
题意:
一共有三种操作:破坏一个村庄,修复上一次毁坏的村庄,查询包括村庄x且没有被毁坏的最大区间。
题解:
有两种方案。
第一种可以二分村庄左边和右边最大的范围,在线段树上查询。复杂度在log*log级别。
第二种不需要二分,线段树维护两个东西:区间左端点向右的最大范围,区间右端点向左的最大范围。
如果一个区间的左端点范围能覆盖x,那么答案是这个范围加上同一层左边区间右端点的范围,反之同理。
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+7;
int n,m,x;
int ll[N<<2],rl[N<<2],len[N<<2];
char s[5];
int pu(int rt){
ll[rt]=ll[rt<<1];
if(ll[rt<<1]==len[rt<<1]) ll[rt]+=ll[rt<<1|1];
rl[rt]=rl[rt<<1|1];
if(rl[rt<<1|1]==len[rt<<1|1]) rl[rt]+=rl[rt<<1];
len[rt]=len[rt<<1]+len[rt<<1|1];
}
void build(int l,int r,int rt){
if(l==r){
ll[rt]=rl[rt]=len[rt]=1;
return;
}
int m=l+r>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
pu(rt);
}
void update(int x,int l,int r,int rt,int val){
if(l==r&&l==x){
ll[rt]=rl[rt]=val;
return;
}
int m=l+r>>1;
if(x<=m) update(x,l,m,rt<<1,val);
else update(x,m+1,r,rt<<1|1,val);
pu(rt);
}
int query(int x,int l,int r,int rt){
if(l==r) return ll[rt];
if(l+ll[rt]-1>=x){
if(l==1) return ll[rt];
else return ll[rt]+rl[rt-1];
}
if(r-rl[rt]+1<=x){
if(r==n) return rl[rt];
else return rl[rt]+ll[rt+1];
}
int m=l+r>>1;
if(x<=m) return query(x,l,m,rt<<1);
else return query(x,m+1,r,rt<<1|1);
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF){
stack<int>st;
build(1,n,1);
while(m--){
scanf("%s",s);
if(s[0]=='D'){
scanf("%d",&x);
st.push(x);
update(x,1,n,1,0);
}
else if(s[0]=='R'){
x=st.top();st.pop();
update(x,1,n,1,1);
}
else{
scanf("%d",&x);
printf("%d\n",query(x,1,n,1));
}
}
}
}
HDU - 3974 - Assign the task
题目链接<http://acm.hdu.edu.cn/showproblem.php?pid=3974>
题意:
n个员工组成一颗单向的树,每次分派给一个员工一个任务,他以及在他下面直接或间接相连的所有员工都得做这个任务。询问是哪个员工当前做的任务是什么。初始化都是-1。
题解:
映射+线段树。
每一个员工能确定一个范围,每修改一个员工就是一次区间修改。
每一个员工也有对应的一个叶子节点,每次查询就是单点查询。
叶子节点的顺序可以看成dfs序。
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+7;
struct Edge{
int v,nxt;
Edge(int v=0,int nxt=0):v(v),nxt(nxt){}
}e[N];
struct Node{
int l,r;
}dm[N];
int p[N],edn;
void add(int u,int v){
e[++edn]=Edge(v,p[u]);p[u]=edn;
}
bool vis[N];
int mp[N];
int n,m,t,cs=0;
int cnt=0;
void init(int u){
cnt++;
mp[u]=dm[u].l=cnt;
if(p[u]==-1){
dm[u].r=cnt;
return;
}
for(int i=p[u];~i;i=e[i].nxt){
int v=e[i].v;
init(v);
dm[u].r=dm[v].r;
}
}
int lazy[N<<2];
void pd(int rt){
if(~lazy[rt]){
lazy[rt<<1]=lazy[rt<<1|1]=lazy[rt];
lazy[rt]=-1;
}
}
void update(int L,int R,int l,int r,int rt,int val){
if(L<=l&&r<=R){
lazy[rt]=val;
return;
}
pd(rt);
int m=l+r>>1;
if(L<=m) update(L,R,l,m,rt<<1,val);
if(m<R) update(L,R,m+1,r,rt<<1|1,val);
}
int query(int x,int l,int r,int rt){
if(~lazy[rt]||l==r){
return lazy[rt];
}
int m=l+r>>1;
if(x<=m) return query(x,l,m,rt<<1);
else return query(x,m+1,r,rt<<1|1);
}
int main()
{
scanf("%d",&t);
while(t--){
memset(vis,false,sizeof(vis));
memset(p,-1,sizeof(p));edn=-1;
memset(lazy,-1,sizeof(lazy));
cnt=0;
scanf("%d",&n);
int u,v;
for(int i=1;i<n;i++){
scanf("%d%d",&v,&u);
add(u,v);vis[v]=true;
}
for(int i=1;i<=n;i++){
if(!vis[i]){
init(i);
break;
}
}
scanf("%d",&m);
printf("Case #%d:\n",++cs);
char s[5];
while(m--){
scanf("%s",s);
if(s[0]=='C'){
scanf("%d",&u);
printf("%d\n",query(mp[u],1,n,1));
}
else{
scanf("%d%d",&u,&v);
update(dm[u].l,dm[u].r,1,n,1,v);
}
}
}
}
【多种修改】HDU - 4578 - Transformation
题目链接<http://acm.hdu.edu.cn/showproblem.php?pid=4578>
题意:
对于一串数字,初始化为零,一共有四种操作:
- 对一段区间加上一个值。
- 对一段区间乘上一个值。
- 把一段区间内每个数字变成同一个值。
- 对一段区间查询
题解:
首先考虑查询,因为的值只有三种,可以对每种都进行维护。
乘上一个值以及变成一个值都很好维护,加法可以拆开来推:
- 平方和:
- 立方和:
还有一个问题,这三种操作之间会互相影响,不能简单的分开维护。可以这么考虑:,也就是说每乘上一个数,区间上add数组也要乘上mul的值。一旦执行第三个操作,那么add和mul都可以回归初始化。这样子的话pushdown数组里的顺序也很明显是3->2->1。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+7;
const int mod=1e4+7;
int p[4][N<<2];
int add[N<<2],mul[N<<2],chg[N<<2];
void ad(int l,int r,int rt,int c){
p[3][rt]=(p[3][rt]+3*c*p[2][rt]%mod+3*c*c%mod*p[1][rt]%mod+c*c%mod*c%mod*(r-l+1)%mod)%mod;
p[2][rt]=(p[2][rt]+2*c*p[1][rt]%mod+c*c%mod*(r-l+1)%mod)%mod;
p[1][rt]=(p[1][rt]+c*(r-l+1)%mod)%mod;
}
void mu(int rt,int c){
p[1][rt]=p[1][rt]*c%mod;
p[2][rt]=p[2][rt]*c%mod*c%mod;
p[3][rt]=p[3][rt]*c%mod*c%mod*c%mod;
}
void cg(int l,int r,int rt,int c){
p[1][rt]=c*(r-l+1)%mod;
p[2][rt]=c*p[1][rt]%mod;
p[3][rt]=c*p[2][rt]%mod;
}
void pd(int l,int r,int rt){
int m=l+r>>1;
if(chg[rt]){
cg(l,m,rt<<1,chg[rt]);
cg(m+1,r,rt<<1|1,chg[rt]);
add[rt<<1]=0;mul[rt<<1]=1;
add[rt<<1|1]=0;mul[rt<<1|1]=1;
chg[rt<<1]=chg[rt<<1|1]=chg[rt];
chg[rt]=0;
}
if(mul[rt]>1){
mu(rt<<1,mul[rt]);
mu(rt<<1|1,mul[rt]);
add[rt<<1]=add[rt<<1]*mul[rt]%mod;
mul[rt<<1]=mul[rt<<1]*mul[rt]%mod;
add[rt<<1|1]=add[rt<<1|1]*mul[rt]%mod;
mul[rt<<1|1]=mul[rt<<1|1]*mul[rt]%mod;
mul[rt]=1;
}
if(add[rt]){
ad(l,m,rt<<1,add[rt]);
ad(m+1,r,rt<<1|1,add[rt]);
add[rt<<1]=(add[rt<<1]+add[rt])%mod;
add[rt<<1|1]=(add[rt<<1|1]+add[rt])%mod;
add[rt]=0;
}
}
void pu(int rt){
p[1][rt]=(p[1][rt<<1]+p[1][rt<<1|1])%mod;
p[2][rt]=(p[2][rt<<1]+p[2][rt<<1|1])%mod;
p[3][rt]=(p[3][rt<<1]+p[3][rt<<1|1])%mod;
}
void build(int l,int r,int rt){
add[rt]=0;mul[rt]=1;chg[rt]=0;
p[1][rt]=p[2][rt]=p[3][rt]=0;
if(l==r) return;
int m=l+r>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
}
void update(int L,int R,int l,int r,int rt,int tp,int c){
if(L<=l&&r<=R){
if(tp==1){
ad(l,r,rt,c);
add[rt]=(add[rt]+c)%mod;
}
else if(tp==2){
mu(rt,c);
add[rt]=add[rt]*c%mod;
mul[rt]=mul[rt]*c%mod;
}
else{
cg(l,r,rt,c);
add[rt]=0;mul[rt]=1;
chg[rt]=c;
}
return;
}
pd(l,r,rt);
int m=l+r>>1;
if(L<=m) update(L,R,l,m,rt<<1,tp,c);
if(m<R) update(L,R,m+1,r,rt<<1|1,tp,c);
pu(rt);
}
int query(int L,int R,int l,int r,int rt,int c){
if(L<=l&&r<=R) return p[c][rt];
pd(l,r,rt);
int m=l+r>>1,res=0;
if(L<=m) res+=query(L,R,l,m,rt<<1,c);
if(m<R) res+=query(L,R,m+1,r,rt<<1|1,c);
return res%mod;
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF){
if(n==0&&m==0) break;
build(1,n,1);
int tp,x,y,c;
while(m--){
scanf("%d%d%d%d",&tp,&x,&y,&c);
if(tp<4) update(x,y,1,n,1,tp,c);
else printf("%d\n",query(x,y,1,n,1,c));
}
}
}
HDU - 4614 - Vases and Flowers
题目链接<https://cn.vjudge.net/problem/45647/origin>
题意:
一共有两个操作:
- 从第A个位置开始放F朵花,有空位就放。直到放完或者没地方放为止。如果一朵花都没有放就输出“Can not put any one.”,否则输出第一朵放花的位置和最后一朵放花的位置。
- 把一段区间的花朵清空。
题解:
如果能够放完,那么就二分可以放花的区间。利用线段树查找确定第一处放花和最后一处放花的位置。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e5+7;
int sum[N<<2],lz[N<<2];
void pu(int rt){
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void pd(int l,int r,int rt){
if(lz[rt]==1){
int m=l+r>>1;
sum[rt<<1]=m-l+1;
sum[rt<<1|1]=r-m;
lz[rt<<1]=lz[rt<<1|1]=1;
lz[rt]=-1;
}
else if(lz[rt]==0){
sum[rt<<1]=sum[rt<<1|1]=0;
lz[rt<<1]=lz[rt<<1|1]=0;
lz[rt]=-1;
}
}
int q(int L,int R,int l,int r,int rt,int flag){
if(L<=l&&r<=R){
int res=sum[rt];
if(flag==1) sum[rt]=r-l+1,lz[rt]=1;
else if(flag==0) sum[rt]=0,lz[rt]=0;
return res;
}
pd(l,r,rt);
int m=l+r>>1,res=0;
if(L<=m) res+=q(L,R,l,m,rt<<1,flag);
if(m<R) res+=q(L,R,m+1,r,rt<<1|1,flag);
pu(rt);
return res;
}
int fdl(int l,int r,int rt,int x){
if(l==r) return l;
pd(l,r,rt);
int m=l+r>>1;
if(sum[rt<<1]<m-l+1&&x<=m){
int res=fdl(l,m,rt<<1,x);
if(res!=-1) return res;
}
if(sum[rt<<1|1]<r-m)
return fdl(m+1,r,rt<<1|1,x);
return -1;
}
int fdr(int l,int r,int rt){
if(l==r) return l;
pd(l,r,rt);
int m=l+r>>1;
if(sum[rt<<1|1]<r-m) return fdr(m+1,r,rt<<1|1);
return fdr(l,m,rt<<1);
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
int n,m;
scanf("%d%d",&n,&m);n--;
memset(sum,0,sizeof(sum));
memset(lz,-1,sizeof(lz));
int k,x,y;
while(m--){
scanf("%d%d%d",&k,&x,&y);
if(k==1){
int ll=fdl(0,n,1,x);
if(ll==-1){
printf("Can not put any one.\n");
continue;
}
int lo=x,hi=n,rr=-1;
while(lo<=hi){
int mid=lo+hi>>1;
int num=mid-x+1-q(x,mid,0,n,1,-1);
if(num>=y) rr=mid,hi=mid-1;
else lo=mid+1;
}
if(rr==-1) rr=fdr(0,n,1);
printf("%d %d\n",ll,rr);
q(ll,rr,0,n,1,1);
}
else printf("%d\n",q(x,y,0,n,1,0));
}
printf("\n");
}
}
HDU - 4553 - 约会安排
题目链接<http://acm.hdu.edu.cn/showproblem.php?pid=4553>
题解:
区间合并。建立两个线段树,一个线段树记录屌丝和女神的区间,一个只记录女神的区间。
线段树维护三个值:左端点向右的最大空间,右端点向左的最大空间,这个区间内的最大空间。lazy标记记录这段区间是不是空的。
#include<bits/stdc++.h>
#define ll long long
#define lc rt<<1
#define rc rt<<1|1
using namespace std;
const int N=2e5+7;
int T,cs;
char s[10];
int n,m;
struct Node{
int l,r,len;
int lz;
}a[2][N<<2];
void build(int l,int r,int rt){
a[0][rt].l=a[0][rt].r=a[0][rt].len=r-l+1;
a[1][rt].l=a[1][rt].r=a[1][rt].len=r-l+1;
a[0][rt].lz=a[1][rt].lz=-1;
if(l==r) return;
int m=l+r>>1;
build(l,m,lc);
build(m+1,r,rc);
}
void pd(int l,int r,int rt,int o){
if(a[o][rt].lz==-1) return;
int tmp;
if(a[o][rt].lz==1){
int m=l+r>>1;
a[o][lc].l=a[o][lc].r=a[o][lc].len=m-l+1;
a[o][rc].l=a[o][rc].r=a[o][rc].len=r-m;
a[o][lc].lz=a[o][rc].lz=1;
a[o][rt].lz=-1;
}
else if(a[o][rt].lz==0){
a[o][lc].l=a[o][lc].r=a[o][lc].len=0;
a[o][rc].l=a[o][rc].r=a[o][rc].len=0;
a[o][lc].lz=a[o][rc].lz=0;
a[o][rt].lz=-1;
}
}
void pu(int l,int r,int rt,int o){
int m=l+r>>1;
a[o][rt].l=a[o][lc].l;
a[o][rt].r=a[o][rc].r;
if(a[o][rt].l>=m-l+1) a[o][rt].l+=a[o][rc].l;
if(a[o][rt].r>=r-m) a[o][rt].r+=a[o][lc].r;
a[o][rt].len=max(a[o][lc].len,a[o][rc].len);
a[o][rt].len=max(a[o][rt].len,max(a[o][rt].l,a[o][rt].r));
a[o][rt].len=max(a[o][rt].len,a[o][lc].r+a[o][rc].l);
}
void update(int L,int R,int l,int r,int rt,int val,int o){
if(L<=l&&r<=R){
if(val) a[o][rt].l=a[o][rt].r=a[o][rt].len=r-l+1;
else a[o][rt].l=a[o][rt].r=a[o][rt].len=0;
a[o][rt].lz=val;
return;
}
pd(l,r,rt,o);
int m=l+r>>1;
if(L<=m) update(L,R,l,m,lc,val,o);
if(m<R) update(L,R,m+1,r,rc,val,o);
pu(l,r,rt,o);
}
int query(int l,int r,int rt,int val,int o){
if(a[o][rt].len<val) return -1;
if(a[o][rt].l>=val) return l;
pd(l,r,rt,o);
int m=l+r>>1;
if(a[o][lc].len>=val) return query(l,m,lc,val,o);
if(a[o][lc].r+a[o][rc].l>=val) return m-a[o][lc].r+1;
return query(m+1,r,rc,val,o);
}
int main()
{
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
printf("Case %d:\n",++cs);
build(1,n,1);
while(m--){
scanf("%s",s);
int x,y;
if(s[0]=='D'){
scanf("%d",&x);
int le=query(1,n,1,x,0);
if(le==-1) printf("fly with yourself\n");
else{
printf("%d,let's fly\n",le);
update(le,le+x-1,1,n,1,0,0);
}
}
else if(s[0]=='N'){
scanf("%d",&x);
int le=query(1,n,1,x,0);
if(le==-1) le=query(1,n,1,x,1);
if(le==-1) printf("wait for me\n");
else{
printf("%d,don't put my gezi\n",le);
update(le,le+x-1,1,n,1,0,0);
update(le,le+x-1,1,n,1,0,1);
}
}
else{
scanf("%d %d",&x,&y);
update(x,y,1,n,1,1,0);
update(x,y,1,n,1,1,1);
printf("I am the hope of chinese chengxuyuan!!\n");
}
}
}
}