温故知新,现在代码水平好了一些,重新刷kuangbin的专题也不那么费劲了。争取理解的透彻一些吧。
区间更新模板:
LL num[maxn],sum[maxn<<2],mark[maxn<<2];
void push_up(int root) {
sum[root] = sum[root<<1]+sum[root<<1|1];
}
void push_down(int root,int cl,int cr) {
if(mark[root]) {
mark[root<<1] += mark[root];
mark[root<<1|1] += mark[root];
sum[root<<1] += mark[root]*cl;
sum[root<<1|1] += mark[root]*cr;
mark[root] = 0;
}
}
void build(int l,int r,int root) { //建树
if(l == r) {
sum[root] = num[l];
return ;
}
int m = (l+r)>>1;
build(l,m,root<<1);
build(m+1,r,root<<1|1); //注意m要+1
push_up(root);
}
void update(int L,int R,LL x,int l,int r,int root) { //成段更新
if(L <= l && r <= R) {
mark[root] += x; //延迟更新标记
sum[root] += (r-l+1)*x;
return ;
}
int m = (l+r)>>1;
push_down(root,m-l+1,r-m); //下推标记
if(m >= L) update(L,R,x,l,m,root<<1);
if(m < R) update(L,R,x,m+1,r,root<<1|1); //注意区间的开闭
push_up(root);
}
LL query(int L,int R,int l,int r,int root) { //区间查询
if(L <= l && r <= R) {
return sum[root];
}
int m = (l+r)>>1;
if(mark[root]) push_down(root,m-l+1,r-m);
LL ans = 0;
if(m >= L) ans += query(L,R,l,m,root<<1);
if(m < R) ans += query(L,R,m+1,r,root<<1|1);
return ans;
}
C - A Simple Problem with Integers
题意:
裸的线段树成段更新。
即两种操作,成段更新,区间查询和
题解:
直接用线段树成段更新的方法即可。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <stack>
#include <bitset>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
#define clr(a,b) memset(a,b,sizeof(a))
#define pb(a) push_back(a)
#define fir first
#define se second
#define LL long long
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int maxn = 1e5+5;
LL mod = 1e9+7;
double eps = 0.00000001;
double PI = acos(-1);
LL num[maxn],sum[maxn<<2],mark[maxn<<2];
void push_up(int root) {
sum[root] = sum[root<<1]+sum[root<<1|1];
}
void push_down(int root,int cl,int cr) {
if(mark[root]) {
mark[root<<1] += mark[root];
mark[root<<1|1] += mark[root];
sum[root<<1] += mark[root]*cl;
sum[root<<1|1] += mark[root]*cr;
mark[root] = 0;
}
}
void build(int l,int r,int root) {
if(l == r) {
sum[root] = num[l];
return ;
}
int m = (l+r)>>1;
build(l,m,root<<1);
build(m+1,r,root<<1|1);
push_up(root);
}
void update(int L,int R,LL x,int l,int r,int root) {
if(L <= l && r <= R) {
mark[root] += x;
sum[root] += (r-l+1)*x;
return ;
}
int m = (l+r)>>1;
push_down(root,m-l+1,r-m);
if(m >= L) update(L,R,x,l,m,root<<1);
if(m < R) update(L,R,x,m+1,r,root<<1|1);
push_up(root);
}
LL query(int L,int R,int l,int r,int root) {
if(L <= l && r <= R) {
return sum[root];
}
int m = (l+r)>>1;
if(mark[root]) push_down(root,m-l+1,r-m);
LL ans = 0;
if(m >= L) ans += query(L,R,l,m,root<<1);
if(m < R) ans += query(L,R,m+1,r,root<<1|1);
return ans;
}
int main() {
int n,q;
scanf("%d%d",&n,&q);
for(int i = 1;i <= n;i++) scanf("%I64d",&num[i]);
build(1,n,1);
clr(mark,0);
for(int i = 1;i <= q;i++) {
char str[3];
int l,r;LL x;
scanf("%s",str);
if(str[0] == 'Q') {
scanf("%d%d",&l,&r);
printf("%I64d\n",query(l,r,1,n,1));
}
else {
scanf("%d%d%I64d",&l,&r,&x);
update(l,r,x,1,n,1);
}
}
}
D - Mayor’s posters (离散化)
题意:
给你一个 1~1e7 的墙,给出一个n,对于每个 i = 1->n , 有两个端点 x1,x2 ,表示x1->x2有的范围有一张新的海报贴在墙上,范围是 x1-x2,问你贴完N个海报后墙上可以看到多少张海报,被完全覆盖的海报不算个数。
题解:
因为范围很大,先对坐标离散化,离散花的时候要注意如果两个坐标距离大于2,那么距离+2,不然是距离+1。
然后用线段树成端更新的作法。
最后我的做法是for循环从1到n都检查一遍,求海报数
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <stack>
#include <bitset>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
#define clr(a,b) memset(a,b,sizeof(a))
#define pb(a) push_back(a)
#define fir first
#define se second
#define LL long long
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int maxn = 1e5+5;
LL mod = 1e9+7;
double eps = 0.00000001;
pii num[maxn],st[maxn],st1[maxn];
int n,col[maxn],f[maxn],dd[maxn],cnt,cc;
void init() {
clr(col,0);
clr(f,0);
cnt = 0;
cc = 0;
}
void push_down(int root) { //延迟更新
col[root<<1] = col[root];
col[root<<1|1] = col[root];
f[root<<1] = f[root];
f[root<<1|1] = f[root];
f[root] = 0;
}
void update(int L,int R,int c,int l,int r,int root) { //成段更新
if(L <= l && r <= R) {
col[root] = c;
f[root] = c;
return ;
}
int m = (l+r)>>1;
if(f[root]) push_down(root);
if(m >= L) update(L,R,c,l,m,root<<1);
if(m < R) update(L,R,c,m+1,r,root<<1|1);
}
int query(int pos,int l,int r,int root) { //单点查询
if(l == r && l == pos) {
return col[root];
}
if(f[root]) push_down(root);
int m = (l+r)>>1;
if(m >= pos) query(pos,l,m,root<<1);
else query(pos,m+1,r,root<<1|1);
}
bool cmp(pii a,pii b) {
if(a.se == b.se) return a.fir < b.fir;
return a.se < b.se;
}
int main() {
int t;
cin>>t;
while(t--) {
scanf("%d",&n);
init();
for(int i = 1;i <= n;i++) {
scanf("%d%d",&num[i].fir,&num[i].se);
st[++cnt] = (make_pair(num[i].fir,i));
st[++cnt] = (make_pair(num[i].se,i));
}
sort(st+1,st+1+cnt);
st[0].fir = st[1].fir-1;
for(int i = 1;i <= cnt;i++) { //离散化
if(st[i].fir - st[i-1].fir == 1) st1[i].fir = ++cc;
else if(st[i].fir - st[i-1].fir > 1) {
cc += 2;
st1[i].fir = cc;
}
else st1[i].fir = cc;
st1[i].se = st[i].se;
}
sort(st1+1,st1+cnt+1,cmp);
for(int i = 1;i <= cnt;i += 2) {
//printf("%d %d - %d %d\n",st1[i].fir,st1[i].se,st1[i+1].fir,st1[i+1].se);
update(st1[i].fir,st1[i+1].fir,i,1,4*n,1);
//for(int i = 1;i <= 4*n;i++) printf("%d-",query(i,1,4*n,1));
//cout<<endl;
}
cnt = 0;
for(int i = 1;i <= 4*n;i++) {
dd[++cnt] = query(i,1,4*n,1);
}
sort(dd+1,dd+1+cnt);
cc = 0;
dd[0] = -1;
for(int i = 1;i <= 4*n;i++)
if(dd[i] != dd[i-1]) cc++;
printf("%d\n",cc-1);
}
}
/*
5
6
2 3
5 6
11 66
22 33
22 33
22 33
*/
F - Count the Colors
题意:
和前面一道题目一样,也是给你一个段,有n个操作,给你l,r,c,在区间l,r之间涂颜色c,问你n次之后有多少个可见颜色段。
题解:
和那道题作法一样,都是区间更新,最后单点询问。这里不区间查询是因为颜色的种类不能区间合并,当然也有区间查询的作法,我用的是单点询问。主要是这道题的线段定义方式不一样。
不过这道题目的区间范围不大,所以我们直接把坐标*2,就可以解决问题。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <stack>
#include <bitset>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
#define clr(a,b) memset(a,b,sizeof(a))
#define pb(a) push_back(a)
#define fir first
#define se second
#define LL long long
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int maxn = 1e6+5;
LL mod = 1e9+7;
double eps = 0.00000001;
int n,col[maxn],f[maxn],dd[maxn],cnt,cc;
void init() {
clr(col,0);
clr(f,0);
}
void push_down(int root) { //延迟更新
col[root<<1] = col[root];
col[root<<1|1] = col[root];
f[root<<1] = f[root];
f[root<<1|1] = f[root];
f[root] = 0;
}
void update(int L,int R,int c,int l,int r,int root) { //成段更新
if(L <= l && r <= R) {
col[root] = c;
f[root] = c;
return ;
}
int m = (l+r)>>1;
if(f[root]) push_down(root);
if(m >= L) update(L,R,c,l,m,root<<1);
if(m < R) update(L,R,c,m+1,r,root<<1|1);
}
int query(int pos,int l,int r,int root) { //单点查询
if(l == r && l == pos) {
return col[root];
}
if(f[root]) push_down(root);
int m = (l+r)>>1;
if(m >= pos) query(pos,l,m,root<<1);
else query(pos,m+1,r,root<<1|1);
}
int main() {
while(scanf("%d",&n) != EOF) {
init();
int mm = 16005;
for(int i = 1;i <= n;i++) {
int l,r,c;
scanf("%d%d%d",&l,&r,&c);
update((l+1)*2,(r+1)*2,c+1,1,mm,1);
//for(int i = 1;i <= 10;i++) printf("%d-",query(i,1,10,1));
//cout<<endl;
}
map<int,int> mp;
int pre = query(1,1,mm,1);
mp[pre]++;
for(int i = 1;i <= mm;i++) {
int tmp = query(i,1,mm,1);
//printf("%d-",tmp);
if(tmp != pre) mp[tmp]++;
pre = tmp;
}
map<int,int>::iterator ii;
for(ii = mp.begin();ii != mp.end();ii++) {
pii it = (*ii);
if(!it.fir) continue;
printf("%d %d\n",it.fir-1,it.se);
}
printf("\n");
}
}
HDU - 3974 J - Assign the task
题意:
给你n个村庄和m次操作。一开始n个村庄从左到右一排连在一起
也就是:1-2-3-4-5-6-7
D代表把一个村庄摧毁,比如D 3之后变成了:
1-2 3 4-5-6-7
R表示把最近的一次摧毁的村庄恢复
Q x代表询问x村庄和几个村庄相连
题解:
用线段树维护两个数组,R[maxn]和R[maxn],
R[pos]代表这个村庄可以到达的最右边的那个村庄,
L[pos]代表这个村庄可以到达的最左边的那个村庄。
每次更新两个数组即可。
比如样例就是:
7 9
1 1 1 1 1 1 1 –7 7 7 7 7 7 7
D 3
1 1 4 4 4 4 4 –2 2 2 7 7 7 7
D 6
1 1 4 4 4 7 7 –2 2 2 5 5 5 7
D 5
1 1 4 4 6 7 7 –2 2 2 4 4 5 7
Q 4
1
1 1 4 4 6 7 7 –2 2 2 4 4 5 7
Q 5
0
1 1 4 4 6 7 7 –2 2 2 4 4 5 7
R
1 1 4 4 4 7 7 –2 2 2 5 5 5 7
Q 4
2
1 1 4 4 4 7 7 –2 2 2 5 5 5 7
R
1 1 4 4 4 4 4 –2 2 2 7 7 7 7
Q 4
4
1 1 4 4 4 4 4 –2 2 2 7 7 7 7
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <stack>
#include <bitset>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
#define clr(a,b) memset(a,b,sizeof(a))
#define pb(a) push_back(a)
#define fir first
#define se second
#define LL long long
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int maxn =1e5+5;
const int inf = 0x3f3f3f3f;
LL mod = 1e9+7;
double eps = 0.00000001;
int L[maxn<<2],R[maxn<<2],m1[maxn<<2],m2[maxn<<2],n,m;
void build(int l,int r,int root) {
if(l == r) {
L[root] = 1;
R[root] = n;
return ;
}
int m = (l+r)>>1;
build(l,m,root<<1);
build(m+1,r,root<<1|1);
}
void push_down_l(int root) {
L[root<<1] = m1[root];
L[root<<1|1] = m1[root];
m1[root<<1] = m1[root];
m1[root<<1|1] = m1[root];
m1[root] = 0;
}
void update_l(int left,int right,int num,int l,int r,int root) {
if(left <= l && r <= right) {
L[root] = num;
m1[root] = num;
return ;
}
if(m1[root]) push_down_l(root);
int m = (l+r)>>1;
if(m >= left) update_l(left,right,num,l,m,root<<1);
if(m < right) update_l(left,right,num,m+1,r,root<<1|1);
}
int query_l(int pos,int l,int r,int root) {
if(l == r && pos == l) {
if(m1[root]) return m1[root];
else return L[root];
}
if(m1[root]) push_down_l(root);
int m = (l+r)>>1;
if(m >= pos) query_l(pos,l,m,root<<1);
else query_l(pos,m+1,r,root<<1|1);
}
void push_down_r(int root) {
R[root<<1] = m2[root];
R[root<<1|1] = m2[root];
m2[root<<1] = m2[root];
m2[root<<1|1] = m2[root];
m2[root] = 0;
}
void update_r(int left,int right,int num,int l,int r,int root) {
if(left <= l && r <= right) {
R[root] = num;
m2[root] = num;
return ;
}
if(m2[root]) push_down_r(root);
int m = (l+r)>>1;
if(m >= left) update_r(left,right,num,l,m,root<<1);
if(m < right) update_r(left,right,num,m+1,r,root<<1|1);
}
int query_r(int pos,int l,int r,int root) {
if(l == r && pos == l) {
if(m2[root]) return m2[root];
else return R[root];
}
if(m2[root]) push_down_r(root);
int m = (l+r)>>1;
if(m >= pos) query_r(pos,l,m,root<<1);
else query_r(pos,m+1,r,root<<1|1);
}
int main() {
while(scanf("%d%d",&n,&m) != EOF) {
build(1,n,1);
stack<int> q;
clr(m1,0);
clr(m2,0);
/*for(int i = 1;i <= n;i++) printf("%d ",query_l(i,1,n,1));
printf("--");
for(int i = 1;i <= n;i++) printf("%d ",query_r(i,1,n,1));
cout<<endl;*/
for(int i = 1;i <= m;i++) {
char str[2];int pos;
scanf("%s",str);
if(str[0] == 'D') {
scanf("%d",&pos);
int ll = query_l(pos,1,n,1),rr = query_r(pos,1,n,1);
if(pos <= rr) update_l(pos,rr,pos+1,1,n,1);
if(pos >= ll) update_r(ll,pos,pos-1,1,n,1);
q.push(pos);
}
else if(str[0] == 'R') {
if(q.empty()) continue;
pos = q.top();q.pop();
int ll,rr;
if(pos > 1) ll = query_l(pos-1,1,n,1);
else ll = 1;
if(pos < n) rr = query_r(pos+1,1,n,1);
else rr = n;
if(pos <= rr) update_l(pos,rr,ll,1,n,1);
if(pos >= ll) update_r(ll,pos,rr,1,n,1);
}
else {
scanf("%d",&pos);
int ll = query_l(pos,1,n,1),rr = query_r(pos,1,n,1);
int ans = rr-ll+1;
if(ans <= 0) ans = 0;
printf("%d\n",ans);
}
/*for(int i = 1;i <= n;i++) printf("%d ",query_l(i,1,n,1));
printf("--");
for(int i = 1;i <= n;i++) printf("%d ",query_r(i,1,n,1));
cout<<endl;*/
}
}
}
HDU - 4578 K - Transformation
题意:
很裸的线段树,题意也很明显。不再赘述
题解:
这道题目一共有四种操作和三种询问,加在一起就很麻烦。
首先要知道push_down操作的顺序。
先更新op = 3,也就是替换操作。因为替换操作后之前的所有操作都不算了,也就是可以直接往下推,把op = 1和op = 2的标记清空。
再更新op = 2,也就是乘操作。因为更新完替换操作之后,乘操作也可以直接往下推,同时更新下面的加标记和乘标记。
最后更新op = 1,也就是加操作。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <stack>
#include <bitset>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
#define clr(a,b) memset(a,b,sizeof(a))
#define pb(a) push_back(a)
#define fir first
#define se second
#define LL long long
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int maxn =1e5+5;
const int inf = 0x3f3f3f3f;
LL mod = 10007;
double eps = 0.00000001;
struct node {
LL sum1,sum2,sum3;
int mark1,mark2,mark3;
} rt[maxn<<2];
void push_up(int root) {
rt[root].sum1 = (rt[root<<1].sum1+rt[root<<1|1].sum1)%mod;
rt[root].sum2 = (rt[root<<1].sum2+rt[root<<1|1].sum2)%mod;
rt[root].sum3 = (rt[root<<1].sum3+rt[root<<1|1].sum3)%mod;
}
void push_down(int root,int cl,int cr) {
if(rt[root].mark1) { //这里先更新替换操作,因为替换操作同时也可以直接更新乘和加操作
LL c = rt[root].mark1;
rt[root<<1].mark1 = rt[root<<1|1].mark1 = c;
rt[root<<1].mark3 = rt[root<<1|1].mark3 = 0;
rt[root<<1].mark2 = rt[root<<1|1].mark2 = 1;
rt[root].mark1 = 0;
rt[root<<1].sum1 = cl * c % mod;
rt[root<<1].sum2 = cl * c * c % mod;
rt[root<<1].sum3 = cl * c * c * c % mod;
rt[root<<1|1].sum1 = cr * c % mod;
rt[root<<1|1].sum2 = cr * c * c % mod;
rt[root<<1|1].sum3 = cr * c * c * c % mod;
}
if(rt[root].mark2 != 1) {
LL c = rt[root].mark2;
rt[root<<1].mark2 = rt[root<<1].mark2 * c % mod; //更新子树的乘和加的标记
rt[root<<1].mark3 = rt[root<<1].mark3 * c % mod;
rt[root<<1|1].mark2 = rt[root<<1|1].mark2 * c % mod;
rt[root<<1|1].mark3 = rt[root<<1|1].mark3 * c % mod;
rt[root].mark2 = 1;
rt[root<<1].sum1 = rt[root<<1].sum1 * c % mod; //更新子树的乘和加的和
rt[root<<1].sum2 = rt[root<<1].sum2 * c * c % mod;
rt[root<<1].sum3 = rt[root<<1].sum3 * c * c * c % mod;
rt[root<<1|1].sum1 = rt[root<<1|1].sum1 * c % mod;
rt[root<<1|1].sum2 = rt[root<<1|1].sum2 * c * c % mod;
rt[root<<1|1].sum3 = rt[root<<1|1].sum3 * c * c * c % mod;
}
if(rt[root].mark3) {
LL c = rt[root].mark3;
rt[root<<1].mark3 = (rt[root<<1].mark3 + c) % mod;
rt[root<<1|1].mark3 = (rt[root<<1|1].mark3 + c) % mod;
rt[root].mark3 = 0;
LL lx1 = rt[root<<1].sum1,lx2 = rt[root<<1].sum2,lx3 = rt[root<<1].sum3;
LL rx1 = rt[root<<1|1].sum1,rx2 = rt[root<<1|1].sum2,rx3 = rt[root<<1|1].sum3;
rt[root<<1].sum1 = (lx1 + cl*c) % mod; //更新子树的加的和
rt[root<<1].sum2 = (lx2 + 2*c*lx1 + cl*c*c) % mod;
rt[root<<1].sum3 = (lx3 + 3*lx2*c + 3*lx1*c*c + cl*c*c*c) % mod;
rt[root<<1|1].sum1 = (rx1 + cr*c) % mod;
rt[root<<1|1].sum2 = (rx2 + 2*c*rx1 + cr*c*c) % mod;
rt[root<<1|1].sum3 = (rx3 + 3*rx2*c + 3*rx1*c*c + cr*c*c*c) % mod;
}
}
void build(int l,int r,int root) {
if(l == r) {
rt[root].sum1 = rt[root].sum2 = rt[root].sum3 = 0;
rt[root].mark1 = rt[root].mark3 = 0;
rt[root].mark2 = 1;
return ;
}
int m = (l+r)>>1;
build(l,m,root<<1);
build(m+1,r,root<<1|1);
rt[root].sum1 = rt[root].sum2 = rt[root].sum3 = 0;
rt[root].mark1 = rt[root].mark3 = 0;
rt[root].mark2 = 1;
}
void update_chg(int L,int R,int c,int l,int r,int root) { //更新替换操作
if(L <= l && r <= R) {
LL dis = r-l+1;
rt[root].sum1 = dis * c % mod;
rt[root].sum2 = dis * c * c % mod;
rt[root].sum3 = dis * c * c * c % mod;
rt[root].mark1 = c; //因为替换了,其他的标记就可以直接清空
rt[root].mark2 = 1;
rt[root].mark3 = 0;
//printf("%d - %d --- %d\n",l,r,rt[root].sum1);
return ;
}
int m = (l+r)>>1;
push_down(root,m-l+1,r-m);
if(m >= L) update_chg(L,R,c,l,m,root<<1);
if(m < R) update_chg(L,R,c,m+1,r,root<<1|1);
push_up(root);
}
void update_mul(int L,int R,int c,int l,int r,int root) { //更新乘操作
if(L <= l && r <= R) {
LL x1 = rt[root].sum1,x2 = rt[root].sum2,x3 = rt[root].sum3;
rt[root].sum3 = (x3 * c * c * c) % mod;
rt[root].sum2 = (x2 * c * c) % mod;
rt[root].sum1 = (x1 * c) % mod;
rt[root].mark2 = rt[root].mark2 * c % mod; //如果之前已经有加操作了,就把它直接乘c
rt[root].mark3 = rt[root].mark3 * c % mod;
return ;
}
int m = (l+r)>>1;
push_down(root,m-l+1,r-m);
if(m >= L) update_mul(L,R,c,l,m,root<<1);
if(m < R) update_mul(L,R,c,m+1,r,root<<1|1);
push_up(root);
}
void update_add(int L,int R,int c,int l,int r,int root) { //更新加操作
if(L <= l && r <= R) {
LL x1 = rt[root].sum1,x2 = rt[root].sum2,x3 = rt[root].sum3,dis = r-l+1;
rt[root].sum3 = (x3 + 3*x2*c + 3*x1*c*c + dis*c*c*c) % mod;
rt[root].sum2 = (x2 + 2*c*x1 + dis*c*c) % mod;
rt[root].sum1 = (x1 + dis*c) % mod;
rt[root].mark3 = (rt[root].mark3 + c) % mod;
return ;
}
int m = (l+r)>>1;
push_down(root,m-l+1,r-m);
if(m >= L) update_add(L,R,c,l,m,root<<1);
if(m < R) update_add(L,R,c,m+1,r,root<<1|1);
push_up(root);
}
LL query(int L,int R,int c,int l,int r,int root) {
if(L <= l && r <= R) {
if(c == 1) return rt[root].sum1;
if(c == 2) return rt[root].sum2;
if(c == 3) return rt[root].sum3;
}
int m = (l+r)>>1;
push_down(root,m-l+1,r-m);
LL ans = 0;
if(m >= L) ans += query(L,R,c,l,m,root<<1);
if(m < R) ans += query(L,R,c,m+1,r,root<<1|1);
return (ans%mod);
}
main() {
//freopen("in.txt","r",stdin);
//freopen("out1.txt","w",stdout);
int n,m;
while(scanf("%d%d",&n,&m) != EOF && (n || m)) {
build(1,n,1);
for(int i = 1;i <= m;i++) {
int op,l,r,c;
scanf("%d%d%d%d",&op,&l,&r,&c);
if(op == 1) update_add(l,r,c,1,n,1);
if(op == 2) update_mul(l,r,c,1,n,1);
if(op == 3) update_chg(l,r,c,1,n,1);
if(op == 4) printf("%I64d\n",query(l,r,c,1,n,1)%mod);
//for(int i = 1;i <= n;i++) printf("%I64d ",query(i,i,1,1,n,1));
//cout<<endl;
}
}
}
/*
5 4
3 1 3 5
3 1 2 8
3 4 5 3
3 2 2 7
*/
HDU - 4614 L - Vases and Flowers (二分+线段树)
题意:
n个花瓶,m次操作。
op = 1,从pos = k开始一直到pos = n,给你x朵花,碰到空花瓶就插花进去,直到花插完或者到了pos = n,如果多的花就丢了。
op = 2,给你l,r,问你当前l ~ r之间的花瓶内有多少朵花,并且把l ~ r之间的花瓶全部清空
题解:
首先,op = 2的时候,直接查询l ~ r之间的sum就可以,然后再update区间l ~ r的花为0
之后是op = 1。因为每个花瓶只能放一朵花,那么我们查询某个区间的sum,再减去区间内的花瓶数,就可以知道这个区间可以放多少朵花。知道这个之后,我们先求出从k ~ n我们可以查多少朵花,也就是nn = min(x , n-k+1 - query(k,n,1,n,1))
,然后在区间k ~ n二分求两个点,刚好可以插一朵花和可以插nn朵花。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <stack>
#include <bitset>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
#define clr(a,b) memset(a,b,sizeof(a))
#define pb(a) push_back(a)
#define fir first
#define se second
#define LL long long
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int maxn =1e5+5;
const int inf = 0x3f3f3f3f;
LL mod = 10007;
double eps = 0.00000001;
int n,m,sum[maxn<<2],mark[maxn<<2];
void push_up(int root) {
sum[root] = sum[root<<1]+sum[root<<1|1];
}
void push_down(int root,int cl,int cr) {
mark[root<<1] = mark[root];
mark[root<<1|1] = mark[root];
sum[root<<1] = cl*mark[root];
sum[root<<1|1] = cr*mark[root];
mark[root] = -1;
}
void build(int l,int r,int root) {
if(l == r) {
sum[root] = 0;
mark[root] = -1;
return ;
}
int m = (l+r)>>1;
build(l,m,root<<1);
build(m+1,r,root<<1|1);
push_up(root);
mark[root] = -1;
}
void update(int L,int R,int c,int l,int r,int root) {
if(L <= l && r <= R) {
sum[root] = (r-l+1)*c;
mark[root] = c;
return ;
}
int m = (l+r)>>1;
if(mark[root] != -1) push_down(root,m-l+1,r-m);
if(m >= L) update(L,R,c,l,m,root<<1);
if(m < R) update(L,R,c,m+1,r,root<<1|1);
push_up(root);
}
int query(int L,int R,int l,int r,int root) {
if(L <= l && r <= R) {
return sum[root];
}
int m = (l+r)>>1;
if(mark[root] != -1) push_down(root,m-l+1,r-m);
int ans = 0;
if(m >= L) ans += query(L,R,l,m,root<<1);
if(m < R) ans += query(L,R,m+1,r,root<<1|1);
push_up(root);
return ans;
}
int get_pos(int s,int x) {
int l = s,r = n;
if(x == 1 && query(s,s,1,n,1) == 0) return s;
while(l + 1 < r) {
int m = (l+r)>>1;
if(m-s+1 - query(s,m,1,n,1) >= x) r = m;
else l = m;
//printf("%d-%d--%d\n",l,r,x);
}
return r;
}
int main() {
int t;
scanf("%d",&t);
while(t--) {
scanf("%d%d",&n,&m);
build(1,n,1);
for(int i = 1;i <= m;i++) {
int op,l,r;
scanf("%d%d%d",&op,&l,&r);
l++,r;
if(op == 1) {
int cnt = query(l,n,1,n,1);
//printf("%d++++",cnt);
if(cnt == n-l+1) {
puts("Can not put any one.");
continue;
}
int nn = min(r,n-l+1-cnt);
int ll = get_pos(l,1),rr = get_pos(l,nn);
update(ll,rr,1,1,n,1);
printf("%d %d\n",ll-1,rr-1);
}
else {
r++;
printf("%d\n",query(l,r,1,n,1));
update(l,r,0,1,n,1);
}
//for(int i = 1;i <= n;i++) printf("%d ",query(i,i,1,n,1));
//cout<<endl;
}
cout<<endl;
}
}
HDU - 4553 M - 约会安排(区间合并)
题意:
给你长度为T的时间,m次询问
每次询问有三种情况:
屌丝,分配最近的长度为x还没有占用一块连续区间给他
女神,也是分配最近的长度为x还没有占用一块连续区间给他,但是如果找不到区间,就再去无视屌丝的占用再查询一次。
清空L-R之间所有的时间占用。
题解:
用到线段树的区间合并思想,维护每个区间对于屌丝的还没有占用的长度,dl,dr,dm。dl是该区间左边连续的空区间,dr是该区间右边连续的空区间,dm是该区间最长的连续的空区间。
对于女神就是nl,nr和nm。再用一个mark当懒惰标记。因为每一种更新都可以直接覆盖之前的更新,所以就只要一个标记,用不同的数字表示即可。1表示屌丝,2
表示女神,3表示清空。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <stack>
#include <bitset>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
#define clr(a,b) memset(a,b,sizeof(a))
#define pb(a) push_back(a)
#define fir first
#define se second
#define LL long long
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int maxn =1e5+5;
const int inf = 0x3f3f3f3f;
LL mod = 10007;
double eps = 0.00000001;
int n,m;
struct node {
int dl,dr,dm,nl,nr,nm,mark;
} rt[maxn<<2];
void build(int l,int r,int root) { //建树,主要是初始化
if(l == r) {
rt[root].dl = rt[root].dr = rt[root].dm = 1;
rt[root].nl = rt[root].nr = rt[root].nm = 1;
rt[root].mark = 0;
return ;
}
int m = (l+r)>>1;
build(l,m,root<<1);
build(m+1,r,root<<1|1);
rt[root].dl = rt[root].dr = rt[root].dm = r-l+1;
rt[root].nl = rt[root].nr = rt[root].nm = r-l+1;
rt[root].mark = 0;
}
void push_up(int root,int cl,int cr) { //区间合并
if(rt[root<<1].dl == cl) rt[root].dl = rt[root<<1].dl + rt[root<<1|1].dl;
else rt[root].dl = rt[root<<1].dl;
if(rt[root<<1|1].dr == cr) rt[root].dr = rt[root<<1].dr + rt[root<<1|1].dr;
else rt[root].dr = rt[root<<1|1].dr;
rt[root].dm = max((rt[root<<1].dr+rt[root<<1|1].dl) , max(rt[root<<1].dm,rt[root<<1|1].dm));
if(rt[root<<1].nl == cl) rt[root].nl = rt[root<<1].nl + rt[root<<1|1].nl;
else rt[root].nl = rt[root<<1].nl;
if(rt[root<<1|1].nr == cr) rt[root].nr = rt[root<<1].nr + rt[root<<1|1].nr;
else rt[root].nr = rt[root<<1|1].nr;
rt[root].nm = max((rt[root<<1].nr+rt[root<<1|1].nl) , max(rt[root<<1].nm,rt[root<<1|1].nm));
}
void push_down(int root,int cl,int cr) { //根据不同标记,下推结果
if(rt[root].mark == 1) {
rt[root<<1].dl = rt[root<<1].dr = rt[root<<1].dm = 0;
rt[root<<1].nl = rt[root<<1].nr = rt[root<<1].nm = cl;
rt[root<<1|1].dl = rt[root<<1|1].dr = rt[root<<1|1].dm = 0;
rt[root<<1|1].nl = rt[root<<1|1].nr = rt[root<<1|1].nm = cr;
}
if(rt[root].mark == 2) {
rt[root<<1].dl = rt[root<<1].dr = rt[root<<1].dm = 0;
rt[root<<1].nl = rt[root<<1].nr = rt[root<<1].nm = 0;
rt[root<<1|1].dl = rt[root<<1|1].dr = rt[root<<1|1].dm = 0;
rt[root<<1|1].nl = rt[root<<1|1].nr = rt[root<<1|1].nm = 0;
}
if(rt[root].mark == 3) {
rt[root<<1].dl = rt[root<<1].dr = rt[root<<1].dm = cl;
rt[root<<1].nl = rt[root<<1].nr = rt[root<<1].nm = cl;
rt[root<<1|1].dl = rt[root<<1|1].dr = rt[root<<1|1].dm = cr;
rt[root<<1|1].nl = rt[root<<1|1].nr = rt[root<<1|1].nm = cr;
}
rt[root<<1].mark = rt[root<<1|1].mark = rt[root].mark;
rt[root].mark = 0;
}
void update(int L,int R,int op,int l,int r,int root) {
if(L <= l && r <= R) {
if(op == 1) {
rt[root].dl = rt[root].dr = rt[root].dm = 0;
rt[root].nl = rt[root].nr = rt[root].nm = r-l+1;
}
if(op == 2) {
rt[root].dl = rt[root].dr = rt[root].dm = 0;
rt[root].nl = rt[root].nr = rt[root].nm = 0;
}
if(op == 3) {
rt[root].dl = rt[root].dr = rt[root].dm = r-l+1;
rt[root].nl = rt[root].nr = rt[root].nm = r-l+1;
}
rt[root].mark = op;
return ;
}
int m = (l+r)>>1;
if(rt[root].mark) push_down(root,m-l+1,r-m);
if(m >= L) update(L,R,op,l,m,root<<1);
if(m < R) update(L,R,op,m+1,r,root<<1|1);
push_up(root,m-l+1,r-m);
}
int query(int len,int op,int l,int r,int root) {
//printf("%d--%d\n",l,r);
if(l == r) return l;
int m = (l+r)>>1;
if(rt[root].mark) push_down(root,m-l+1,r-m);
if(op == 1) {
//printf("%d ++++ %d\n",rt[root<<1].dm,len);
if(rt[root<<1].dm >= len) return query(len,op,l,m,root<<1);
else if(rt[root<<1].dr + rt[root<<1|1].dl >= len) return m-rt[root<<1].dr+1;
else return query(len,op,m+1,r,root<<1|1);
}
else if(op == 2) {
if(rt[root<<1].nm >= len) return query(len,op,l,m,root<<1);
else if(rt[root<<1].nr + rt[root<<1|1].nl >= len) return m-rt[root<<1].nr+1;
else return query(len,op,m+1,r,root<<1|1);
}
}
int main() {
int t,tt = 0;
scanf("%d",&t);
while(t--) {
scanf("%d%d",&n,&m);
build(1,n,1);
printf("Case %d:\n",++tt);
for(int i = 1;i <= m;i++) {
char str[15];
int x,l,r;
scanf("%s",str);
if(str[0] == 'D') {
scanf("%d",&x);
if(rt[1].dm >= x) {
int pos = query(x,1,1,n,1);
update(pos,pos+x-1,1,1,n,1);
printf("%d,let's fly\n",pos);
}
else puts("fly with yourself");
}
else if(str[0] == 'N') {
scanf("%d",&x);
if(rt[1].dm >= x) {
int pos = query(x,1,1,n,1);
printf("%d,don't put my gezi\n",pos);
update(pos,pos+x-1,2,1,n,1);
}
else if(rt[1].nm >= x) {
int pos = query(x,2,1,n,1);
printf("%d,don't put my gezi\n",pos);
update(pos,pos+x-1,2,1,n,1);
}
else puts("wait for me");
}
else {
scanf("%d%d",&l,&r);
update(l,r,3,1,n,1);
puts("I am the hope of chinese chengxuyuan!!");
}
}
}
}
HDU - 1542 P - Atlantis (扫描线)
题意:
一共有n个矩形,接下来n行,分别是第 i 个矩形的左下角和右上角坐标,问你这些矩形叠在一起的总面积是多少。
题解:
线段树扫描线模板题。
扫描线的思想是把所有的线都转化成横线,并且按高度排好序。每次把当前的高度所有有效的横线*高度就是面积。当前高度的有效横线的长度就用线段树维护,而且先要用map什么的离散化一下。不是很难的算法。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <stack>
#include <bitset>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
#define clr(a,b) memset(a,b,sizeof(a))
#define pb(a) push_back(a)
#define fir first
#define se second
#define LL long long
typedef pair<int,int> pii;
typedef pair<LL,int> pli;
typedef pair<LL,LL> pll;
const int maxn = 205;
const int inf = 0x3f3f3f3f;
LL mod = 10007;
double eps = 0.00000001;
int n,mark[maxn<<2];
double sum[maxn<<2],has[maxn];
map<double,int> mp;
struct node {
double l,r,h; //横线的左边,右边和高度
int d; //d为1代表矩形底边,-1代表顶边
node(){}
node(double x1,double x2,double h1,int dd):l(x1),r(x2),h(h1),d(dd){}
} s[maxn];
bool cmp(node a,node b) {
return a.h < b.h;
}
void push_up(int l,int r,int root) {
if(mark[root]) sum[root] = has[r+1] - has[l];
else if(l == r) sum[root] = 0;
else sum[root] = sum[root<<1] + sum[root<<1|1];
}
void update(int L,int R,int d,int l,int r,int root) {
if(L <= l && r <= R) {
mark[root] += d;
push_up(l,r,root);
return ;
}
int m = (l+r)>>1;
if(m >= L) update(L,R,d,l,m,root<<1);
if(m < R) update(L,R,d,m+1,r,root<<1|1);
push_up(l,r,root);
}
int main() {
int tt = 0;
while(scanf("%d",&n) != EOF && n) {
mp.clear();
for(int i = 1;i <= n;i++) {
double x1,y1,x2,y2;
scanf("%lf%lf%lf%lf",&x1,&y1,&x2,&y2);
s[i*2-1] = node(x1,x2,y1,1);
s[i*2] = node(x1,x2,y2,-1); //把点都化成横线的形式
mp[x1] = 1;
mp[x2] = 1; //离散化横坐标
}
int cnt = 0;
has[0] = 0;
for(auto it : mp) { //离散化并存到has数组里
double tmp = it.fir;
mp[tmp] = ++cnt;
has[cnt] = tmp;
}
sort(s+1,s+1+n*2,cmp); //按高度对横线排序
double ans = 0;
clr(sum,0);
clr(mark,0);
for(int i = 1;i <= n*2;i++) {
update(mp[s[i].l],mp[s[i].r]-1,s[i].d,1,cnt,1); //用线段树维护横线的长度。
ans += sum[1]*(s[i+1].h - s[i].h);
}
printf("Test case #%d\nTotal explored area: %.2f\n\n",++tt,ans);
}
}