啊。。这个专题才开的时候,学校就恢复了vj挂题的制度,加上各种比赛,以及文化课之类的,导致这个专题写的好慢。。。写完后也一直没时间总结博客。。就是懒…
-------------------------------------------------------------------------
感觉…自己越来越没有斗志了…天天像个废物一样,就只会写几个傻瓜题,比赛也是签个到就开始罚坐。。学个新的图论算法几乎要自闭,,
图论也太难了吧!!!
图论也太难了吧!!!
图论也太难了吧!!!
二分图写着写着就遇到了HK算法,然后看不懂,,只会套板子,写着写着觉得放到暑假集训再处理这个二分图吧,不如先去把次小生成树完善一下,,然后写了四个题就遇到了最小树形图。。。再次自闭。。。。
还是先把这个线段树专题总结一下吧
-------------------------------------------------------------------------
就三个题没补
一个是多重lazy标记。。写吐了。真不想写这个(不会。。
还有一个是扫描线处理周长问题(只学了处理面积,周长一时半会没看会。
最后一个就是很玄学的看都看不懂的题目。。说是三维线段树????
我太菜了…
-------------------------------------------------------------
开始正文
专题链接
A - 敌兵布阵 HDU - 1166
单点修改 区间查询
最简单的线段树操作了,不如放个树状数组上来吧
int lowbit(int x){return x&(-x);}
int arr[maxn];
void updata(int pos,int val,int n){
while(pos<=n){
arr[pos]+=val;
pos+=lowbit(pos);
}
}
int query(int pos){
int ans=0;
while(pos>0){
ans+=arr[pos];
pos-=lowbit(pos);
}
return ans;
}
int cas=0;
void solve(){
ms(arr,0);
int n;cin>>n;
rep(i,1,n){
int tmp;cin>>tmp;
updata(i,tmp,n);
}
string ope;
int a,b;
cout<<"Case "<<++cas<<":"<<endl;
while(cin>>ope&&ope[0]!='E'){
cin>>a>>b;
if(ope[0]=='A'){
updata(a,b,n);
}else if(ope[0]=='S'){
updata(a,-b,n);
}else{
cout<<query(b)-query(a-1)<<endl;
}
}
}
B - I Hate It HDU - 1754
区间查询,单点更新
还是基本操作。。上题放树状数组,那这个就放线段树把
struct node{
int l,r;
int val;
}tree[maxn<<2];
void build(int rt,int l,int r){
tree[rt].l=l;
tree[rt].r=r;
if(l==r){
scanf("%d",&tree[rt].val);
return ;
}
build(lson);
build(rson);
tree[rt].val=max(tree[ls].val,tree[rs].val);
}
void updata(int rt,int a,int b){
int l=tree[rt].l,r=tree[rt].r;
if(l==r&&r==a){
tree[rt].val=b;
}else{
if(a<=md){
updata(ls,a,b);
}else{
updata(rs,a,b);
}
tree[rt].val=max(tree[ls].val,tree[rs].val);
}
}
int query(int rt,int a,int b){
int l=tree[rt].l,r=tree[rt].r;
if(l==a&&r==b){
return tree[rt].val;
}else{
if(b<=md){
return query(ls,a,b);
}else if(a>=md+1){
return query(rs,a,b);
}else{
return max(query(ls,a,md),query(rs,md+1,b));
}
}
}
void solve(){
int n,m;while(~scanf("%d %d",&n,&m)){
build(1,1,n);
while(m--){
char str[30];int a,b;
scanf("%s %d %d",str,&a,&b);
if(str[0]=='Q'){
cout<<query(1,a,b)<<endl;
}else{
updata(1,a,b);
}
}
}
}
C - A Simple Problem with Integers POJ - 3468
嗯。。区间更新,区间查询
考察的是lazy标记的思想,单点修改必定会超时的
算是lazy标记入门题
我想不明白的是。。用scanf就一直wa…改成cin…就过了
迷惑。。。
struct node{
ll l,r;
ll val,tag;
}tree[maxn<<2];
void build(ll rt,ll l,ll r){
tree[rt].l=l;
tree[rt].r=r;
tree[rt].tag=0;
if(l==r){
cin>>tree[rt].val;
}else{
build(lson);
build(rson);
tree[rt].val=tree[ls].val+tree[rs].val;
}
}
inline void push_down(ll rt){
if(!tree[rt].tag){
return ;
}
ll l=tree[rt].l,r=tree[rt].r;
tree[ls].tag+=tree[rt].tag;
tree[rs].tag+=tree[rt].tag;
tree[ls].val+=(md-l+1)*tree[rt].tag;
tree[rs].val+=(r-md)*tree[rt].tag;
tree[rt].tag=0;
}
void updata(ll rt,ll a,ll b,ll c){
ll l=tree[rt].l,r=tree[rt].r;
if(l==a&&r==b){
tree[rt].tag+=c;
tree[rt].val+=(r-l+1)*c;
return ;
}else{
push_down(rt);
if(b<=md){
updata(ls,a,b,c);
}else if(a>=md+1){
updata(rs,a,b,c);
}else{
updata(ls,a,md,c);
updata(rs,md+1,b,c);
}
tree[rt].val=tree[ls].val+tree[rs].val;
}
}
ll query(ll rt,ll a,ll b){
ll l=tree[rt].l,r=tree[rt].r;
if(l==a&&r==b){
return tree[rt].val;
}else{
push_down(rt);
if(b<=md){
return query(ls,a,b);
}else if(a>=md+1){
return query(rs,a,b);
}else{
return query(ls,a,md)+query(rs,md+1,b);
}
}
}
void solve(){
ll n,q;
cin>>n>>q;
build(1,1,n);
while(q--){
char ope[10];
ll a,b,c;
cin>>ope>>a>>b;
if(ope[0]=='Q'){
cout<<query(1,a,b)<<endl;
}else{
cin>>c;
updata(1,a,b,c);
}
}
}
D - Mayor’s posters POJ - 2528
呃。线段树染色+离散化处理
数据太大了,需要离散化一下,不然无法建树
注意这里每一个点代表的是片段的颜色,比如3,4,5,6,7
4,6之间,3,4之间,6,7之间依次染了色,然后你离散化就成了,3,4,6,7,对应1,2,3,4,然后你记录出来的就是1,2有颜色,3,4有颜色,这就忽略了一种颜色!!,原本的应该是,3,4 ,,,5,,6,7
所以离散化的时候,对于每一个长度大于1的区间,就需要额外添加一个数进去来确保正确度!即
for(ll i=1;i<=sz-1;i++){
if(v[i]-v[i-1]>1){
v.pb(v[i-1]+1);
}
}
注意这个题染的是点,和下面那个题不同,这点要区分开
然后就没啥好说的了,上代码吧,注意线段树离散化的操作
struct node{
ll l,r;
ll val;
}tree[maxn<<4];
struct IUPUT{
ll l,r;
}input[maxn];
ll vis[maxn<<4];
vector<ll>v;
ll ans=0;
void build(ll rt,ll l,ll r){
tree[rt].l=l;
tree[rt].r=r;
tree[rt].val=-1;
if(l==r){
return ;
}
build(lson);
build(rson);
}
inline void push_down(ll rt){
ll l=tree[rt].l,r=tree[rt].r;
if(tree[rt].val!=-1&&l!=r){
tree[ls].val=tree[rt].val;
tree[rs].val=tree[rt].val;
tree[rt].val=-1;
}
}
void updata(ll rt,ll a,ll b,ll c){
ll l=tree[rt].l,r=tree[rt].r;
if(l==a&&r==b){
tree[rt].val=c;
return ;
}
push_down(rt);
if(b<=md){
updata(ls,a,b,c);
}else if(a>=md+1){
updata(rs,a,b,c);
}else{
updata(ls,a,md,c);
updata(rs,md+1,b,c);
}
}
void query(ll rt,ll a,ll b){
ll l=tree[rt].l,r=tree[rt].r;
//de(tree[rt].val),de(l),de(r),de(a),de(b);
if(l==a&&r==b&&tree[rt].val!=-1){
ll tmp=tree[rt].val;
if(!vis[tmp]){
vis[tmp]=1;
ans++;
}
return ;
}
if(tree[rt].val==-1&&l==r){
return ;
}
push_down(rt);
if(b<=md){
query(ls,a,b);
}else if(a>=md+1){
query(rs,a,b);
}else{
query(ls,a,md);
query(rs,md+1,b);
}
}
void solve(){
v.clear();
ans=0;
ll n=read();
rep(i,1,n){
input[i].l=read();
input[i].r=read();
v.pb(input[i].l);
v.pb(input[i].r);
}
ll sz=v.size();
sort(v.begin(),v.end());
for(ll i=1;i<=sz-1;i++){
if(v[i]-v[i-1]>1){
v.pb(v[i-1]+1);
}
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
sz=v.size();
build(1,1,sz);
rep(i,1,n){
ll a=lower_bound(v.begin(),v.end(),input[i].l)-v.begin()+1;
ll b=lower_bound(v.begin(),v.end(),input[i].r)-v.begin()+1;
updata(1,a,b,i);
}
ms(vis,0);
query(1,1,sz);
cout<<ans<<endl;
}
E - Just a Hook HDU - 1698
就是个区间更新,区间查询的题目
题意:一个拐杖原本单位长度价值为1,然后每次选定一个
,
区间,把这个区间单位价值修改为
最后求区间之和。。
就是lazy的应用吧
struct node{
int l,r;
int val,tag;
}tree[maxn<<2];
void build(int rt,int l,int r){
tree[rt].l=l;
tree[rt].r=r;
tree[rt].tag=0;
if(l==r){
tree[rt].val=1;
return ;
}
build(lson);
build(rson);
tree[rt].val=tree[ls].val+tree[rs].val;
}
inline void push_down(int rt){
int l=tree[rt].l,r=tree[rt].r;
if(l==r||tree[rt].tag==0){
tree[rt].tag=0;
return ;
}
tree[ls].tag=tree[rs].tag=tree[rt].tag;
tree[ls].val=(md-l+1)*tree[ls].tag;
tree[rs].val=(r-md)*tree[rs].tag;
tree[rt].tag=0;
}
void updata(int rt,int a,int b,int c){
int l=tree[rt].l,r=tree[rt].r;
if(l==a&&r==b){
tree[rt].tag=c;
tree[rt].val=(r-l+1)*c;
return ;
}
push_down(rt);
if(b<=md){
updata(ls,a,b,c);
}else if(a>=md+1){
updata(rs,a,b,c);
}else{
updata(ls,a,md,c);
updata(rs,md+1,b,c);
}
tree[rt].val=tree[ls].val+tree[rs].val;
}
int query(int rt,int a,int b){
int l=tree[rt].l,r=tree[rt].r;
if(l==a&&r==b){
return tree[rt].val;
}
push_down(rt);
if(b<=md){
return query(ls,a,b);
}else if(a>=md+1){
return query(rs,a,b);
}else{
return query(ls,a,md)+query(rs,md+1,b);
}
}
int cas=0;
void solve(){
int n=read();build(1,1,n);
int q=read();while(q--){
int a=read(),b=read(),c=read();
updata(1,a,b,c);
}
printf("Case %d: The total value of the hook is %d.\n",++cas,query(1,1,n));
}
F - Count the Colors ZOJ - 1610
也是线段树染色问题,每次把一个区域染成颜色
,最后求出每种颜色有多少段,输出段数,为0(或者被覆盖完)则不用输出
因为这个染色的是线段,不是点,也即是说【1,2】 和【3,4】是不相邻的
这时候我们可以用
来代替
到
这个区间,更新数值的时候更新
L+1,R的点就可以了,最后暴力找出每个颜色的段数
struct segtree{
int l,r;
int val;
}tree[maxn<<2];
int vis[maxn],ans,cup[maxn];
void build(int rt,int l,int r){
tree[rt].l=l;
tree[rt].r=r;
tree[rt].val=-1;
if(l==r){
return ;
}
build(lson);
build(rson);
}
inline void push_down(int rt){
int l=tree[rt].l,r=tree[rt].r;
if(l==r||tree[rt].val==-1){
return ;
}
tree[ls].val=tree[rs].val=tree[rt].val;
tree[rt].val=-1;
}
void updata(int rt,int a,int b,int c){
//de(l),de(r);
//de(rt);
int l=tree[rt].l,r=tree[rt].r;
//de(a),de(b),de(l),de(r);
if(l==a&&r==b){
tree[rt].val=c;
return ;
}
push_down(rt);
if(b<=md){
updata(ls,a,b,c);
}else if(a>=md+1){
updata(rs,a,b,c);
}else{
updata(ls,a,md,c);
updata(rs,md+1,b,c);
}
}
void query(int rt,int a,int b){
int l=tree[rt].l,r=tree[rt].r;
if(l==a&&r==b&&tree[rt].val!=-1){
for(int i=l;i<=r;i++){
cup[i]=tree[rt].val;
}
return ;
}
if(l==r&&tree[rt].val!=-1){
return ;
}
push_down(rt);
if(b<=md){
query(ls,a,b);
}else if(a>=md+1){
query(rs,a,b);
}else{
query(ls,a,md);
query(rs,md+1,b);
}
}
void solve(){
int n;while(~scanf("%d",&n)){
int N=8001;
build(1,1,N-1);
ms(vis,0),ms(cup,-1);
rep(i,1,n){
int x1,x2,c;
scanf("%d %d %d",&x1,&x2,&c);
x1++;
updata(1,x1,x2,c);
}
query(1,1,N-1);
for(int i=1;i<=N;i++){
if(cup[i-1]!=-1&&(i==1||cup[i-1]!=cup[i-2])){
vis[cup[i-1]]++;
}
}
for(int i=0;i<=N;i++){
if(vis[i]){
printf("%d %d\n",i,vis[i]);
}
}
printf("\n");
}
}
G - Balanced Lineup POJ - 3264
呃,区间最值查询,每次输出一个区间最大值与最小值的差值
简单题》。
struct segtree{
int l,r;
int mx,mn;
}tree[maxn<<2];
int ans1,ans2;
void build(int rt,int l,int r){
tree[rt].l=l;
tree[rt].r=r;
if(l==r){
int tmp=read();
tree[rt].mx=tree[rt].mn=tmp;
return ;
}
build(lson);
build(rson);
tree[rt].mx=max(tree[ls].mx,tree[rs].mx);
tree[rt].mn=min(tree[ls].mn,tree[rs].mn);
}
void query(int rt,int a,int b){
int l=tree[rt].l,r=tree[rt].r;
if(l==a&&r==b){
ans1=max(ans1,tree[rt].mx);
ans2=min(ans2,tree[rt].mn);
return ;
}
if(b<=md){
query(ls,a,b);
}else if(a>=md+1){
query(rs,a,b);
}else{
query(ls,a,md);
query(rs,md+1,b);
}
}
void solve(){
int n=read(),q=read();
build(1,1,n);
while(q--){
int a=read(),b=read();
ans1=-1*INF;
ans2=INF;
query(1,a,b);
printf("%d\n",ans1-ans2);
}
}
H - Can you answer these queries? HDU - 4027
区间更新,区间求和
由于这个区间更新是开方操作。。。所以必然不能用lazy标记了。。
但是暴力会超时?那怎么办
仔细想想,开方操作对于一个整数来说,最多操作6,7次就变成1了,变成1之后的开方就不会再变化了,所以我们还是暴力更新。只不过需要维护一下每个区间是否全为1了,是的话就不需要更新了,区间求和就是常规套路了
struct segtree{
ll l,r;
ll val,be;
}tree[maxn<<2];
void build(int rt,int l,int r){
tree[rt].l=l;
tree[rt].r=r;
tree[rt].be=0;
if(l==r){
scanf("%lld",&tree[rt].val);
if(tree[rt].val==1){
tree[rt].be=1;
}
return ;
}
build(lson);
build(rson);
tree[rt].val=tree[ls].val+tree[rs].val;
tree[rt].be= tree[rt].val==(r-l+1);
}
void updata(ll rt,ll a,ll b){
ll l=tree[rt].l,r=tree[rt].r;
if(l==a&&r==b&&tree[rt].be==1){
return ;
}
if(l==a&&l==r){
tree[rt].val=sqrt(tree[rt].val);
}else if(b<=md){
updata(ls,a,b);
}else if(a>=md+1){
updata(rs,a,b);
}else{
updata(ls,a,md);
updata(rs,md+1,b);
}
if(l != r){
tree[rt].val=tree[ls].val+tree[rs].val;
}
tree[rt].be = tree[rt].val==(r-l+1);
}
ll query(ll rt,ll a,ll b){
ll l=tree[rt].l,r=tree[rt].r;
if(l==a&&r==b){
return tree[rt].val;
} else {
if (b <= md) {
return query(ls,a,b);
} else if (a >= md+1){
return query(rs,a,b);
} else {
return query(ls,a,md) + query(rs,md+1,b);
}
}
}
void solve(){
ll cas=0;
ll n;while(~scanf("%lld",&n)){
build(1,1,n);
ll m;scanf("%lld",&m);
printf("Case #%lld:\n",++cas);
while(m--){
ll a,b,c;scanf("%lld %lld %lld",&a,&b,&c);
if(b>c){
swap(b,c);
}
if (a==0) {
updata(1,b,c);
} else {
printf("%lld\n",query(1,b,c));
}
}
puts("");
}
}
先鸽了…明后天再继续补
——---------------------------------------------------~
I - Tunnel Warfare HDU - 1540
题意:每次可以破坏一个点,或者修复上一个被破坏的点,同时对于每次查询
位置,需要输出
可以和多少个村庄相连,
区间合并的题目,维护每个节点的
,
,
,对于每次破坏操作,用一个栈维护,然后就是区间合并的基本操作了
注意
函数的书写,这点是主要的
inline void push_up(int rt){
int l=tree[rt].l,r=tree[rt].r;
if(l == r){
return ;
}
tree[rt].lx=tree[ls].lx;
tree[rt].rx=tree[rs].rx;
tree[rt].mx = max(max(tree[ls].mx,tree[rs].mx),tree[ls].rx+tree[rs].lx);
if(tree[ls].lx == tree[ls].r - tree[ls].l + 1){
tree[rt].lx += tree[rs].lx;
}
if(tree[rs].rx == tree[rs].r - tree[rs].l + 1){
tree[rt].rx += tree[ls].rx;
}
}
如果左儿子的lx等于左区间长度,那么这个节点lx就可以延伸到右边了
同时还有查询操作的写法,注意分段处理联通情况
struct segtree{
int l,r;
int lx,rx,mx;
}tree[maxn<<2];
void build(int rt,int l,int r){
tree[rt]={l,r,r-l+1,r-l+1,r-l+1};
if(l == r){
return ;
} else {
build(lson);
build(rson);
}
}
inline void push_up(int rt){
int l=tree[rt].l,r=tree[rt].r;
if(l == r){
return ;
}
tree[rt].lx=tree[ls].lx;
tree[rt].rx=tree[rs].rx;
tree[rt].mx = max(max(tree[ls].mx,tree[rs].mx),tree[ls].rx+tree[rs].lx);
if(tree[ls].lx == tree[ls].r - tree[ls].l + 1){
tree[rt].lx += tree[rs].lx;
}
if(tree[rs].rx == tree[rs].r - tree[rs].l + 1){
tree[rt].rx += tree[ls].rx;
}
}
void updata(int rt,int pos,int f){
int l=tree[rt].l,r=tree[rt].r;
if(l==r&&l==pos){
tree[rt]={l,r,f,f,f};
return ;
} else {
if(pos <= md){
updata(ls,pos,f);
}else {
updata(rs,pos,f);
}
push_up(rt);
}
}
int query(int rt,int pos){
int l=tree[rt].l,r=tree[rt].r;
if(l == r || tree[rt].mx == 0 || tree[rt].mx == r-l+1){
return tree[rt].mx;
}
if(pos <= md){
if(pos < md-tree[ls].rx+1 ){
return query(ls,pos);
} else {
return query(ls,pos) + query(rs,md+1);
}
} else {
if (pos > md+1+tree[rs].lx-1){
return query(rs,pos);
} else {
return query(rs,pos)+query(ls,md);
}
}
}
void solve(){
int n,m;while(~scanf("%d %d",&n,&m)){
_stack sta;
char str[10];
build(1,1,n);
int pos;
while(m--){
scanf("%s",str);
if(str[0]=='D'){
scanf("%d",&pos);
updata(1,pos,0);
sta.push(pos);
}else if(str[0]=='Q'){
scanf("%d",&pos);
int ans=query(1,pos);
printf("%d\n",ans);
}else{
if(!sta.empty()){
pos = sta.top();
sta.pop();
updata(1,pos,1);
}
}
}
}
}
J - Assign the task HDU - 3974
好题!
用dfs序来建树,根据关系,我们可以统计每个数的入度,找出入度为0的数(此题唯一),然后用一个in和out数组进行dfs开始建树,对于每个数而言
in和out就是这个数所管理的范围,dfs的过程,就可以把数之间关系用In,out来维护好了
int in[maxn],out[maxn],root[maxn];
vector<int>G[maxn];
struct segtree {
int l,r;
int tag,val;
}tree[maxn<<2];
void dfs(int now,int &time){
time++;
in[now]=time;
int sz=G[now].size();
rep(i,0,sz-1){
dfs(G[now][i],time);
}
time++;
out[now]=time;
}
void build(int rt,int l,int r){
tree[rt].l=l;
tree[rt].r=r;
tree[rt].tag=-1;
tree[rt].val=-1;
if(l == r){
return ;
}
build(lson);
build(rson);
}
inline void push_down(int rt){
int l=tree[rt].l,r=tree[rt].r;
if(l == r || tree[rt].tag == -1){
return ;
} else {
tree[ls].tag = tree[rs].tag = tree[rt].tag;
tree[ls].val = tree[rs].val = tree[rt].val;
tree[rt].tag = -1;
}
}
void updata (int rt,int a,int b,int f){
int l=tree[rt].l,r=tree[rt].r;
if(l == a && r== b){
tree[rt].val = f;
tree[rt].tag = f;
return ;
}
push_down(rt);
if (b <= md){
updata(ls,a,b,f);
} else if(a >= md+1){
updata(rs,a,b,f);
} else {
updata(ls,a,md,f);
updata(rs,md+1,b,f);
}
}
int query(int rt,int pos){
int l=tree[rt].l,r=tree[rt].r;
if(l == r && l == pos){
return tree[rt].val;
} else {
push_down(rt);
if( pos <= md){
return query(ls,pos);
} else {
return query(rs,pos);
}
}
}
int cas=0;
void solve(){
ms(in,0),ms(out,0),ms(root,0);
int n;cin>>n;
rep(i,1,n){
G[i].clear();
}
rep(i,1,n-1){
int u,v;
cin>>u>>v;
G[v].pb(u);
root[u]++;
}
int time=0;
rep(i,1,n){
if(root[i]==0){
dfs(i,time);
break;
}
}
n<<=1;
build(1,1,n);
cout<<"Case #"<<++cas<<":\n";
int m;cin>>m;
while(m--){
char ope[10];
int a,b;
cin>>ope;
//de(ope);
if(ope[0]=='C'){
cin>>a;
int ans=query(1,in[a]);
cout<<ans<<'\n';
}else {
cin>>a>>b;
updata(1,in[a],out[a],b);
}
//getchar();
}
}
K题鸽了
L - Vases and Flowers HDU - 4614
插花,其实就是区间更新,对于每次插花的请求,找出当前这个区间能插花的数目,小于1就输出不可,反之则插满,每次插花二分查找出起点和终点,然后更新数值,lazy的基本功就不说了,清理花就是先查找出共有多少花,然后全部清空就好了
struct segtree {
int l,r;
int val,tag;
} tree[maxn<<2];
void build(int rt, int l, int r) {
tree[rt].l = l;
tree[rt].r = r;
tree[rt].tag = -1;
if (l == r) {
tree[rt].val = 1;
} else {
build(lson);
build(rson);
tree[rt].val = tree[ls].val + tree[rs].val;
}
}
inline void push_down (int rt) {
int l = tree[rt].l, r = tree[rt].r;
if (l == r || tree[rt].tag == -1) {
return ;
} else {
int tmp = tree[rt].tag;
tree[ls].tag = tmp;
tree[rs].tag = tmp;
tree[ls].val = (md - l + 1)*tmp;
tree[rs].val = (r - md)*tmp;
tree[rt].tag = -1;
}
}
void updata(int rt, int a, int b, int c) {
int l = tree[rt].l, r = tree[rt].r;
if(l == a && r == b){
tree[rt].val = (r - l + 1)*c;
tree[rt].tag = c;
return ;
}
push_down(rt);
if (b <= md) {
updata(ls, a, b, c);
} else if (a >= md+1) {
updata(rs, a, b, c);
} else {
updata(ls, a, md, c);
updata(rs, md+1, b, c);
}
tree[rt].val = tree[ls].val + tree[rs].val;
}
int query (int rt, int a, int b) {
int l = tree[rt].l ,r = tree[rt].r ;
if ( l == a && r == b) {
return tree[rt].val;
} else {
push_down(rt);
if ( b <= md) {
return query( ls, a, b);
} else if ( a >= md+1){
return query( rs, a, b);
} else {
return query( ls, a, md) + query( rs, md+1, b);
}
}
}
int cas = 0;
void solve() {
int n,m;scanf("%d %d",&n, &m);
build( 1, 1, n);
while (m--) {
int a,b,ope;
scanf("%d %d %d",&ope, &a, &b);
if ( ope == 1) {
a++;
int num = query(1, a, n);
if (num == 0) {
puts("Can not put any one.");
} else {
num = min(num, b);
int st = a, ed = n;
//left
int L = a, R = n;
while ( L <= R) {
int mid = L + R >> 1;
int tmp = query(1, a, mid);
if ( tmp >= 1) {
st = mid;
R = mid - 1;
} else {
L = mid + 1;
}
}
// right
L = a,R = n;
while (L <= R) {
int mid = L + R >> 1;
int tmp = query(1, a, mid);
if ( tmp >= num) {
ed = mid;
R = mid - 1;
} else {
L = mid + 1;
}
}
printf ("%d %d\n",st-1,ed-1);
updata (1, st, ed, 0);
}
} else {
a++,b++;
int num = query ( 1, a, b);
printf("%d\n",b-a+1-num);
updata (1,a,b,1);
}
}
puts("");
}
M - 约会安排 HDU - 4553
区间合并
维护两个区间,屌丝只更新屌丝,女神同时更新两个
每次查询,优先查询屌丝,然后是女神
注意pushup函数的书写就好了
struct segtree {
int l,r;
int lx,mx,rx;
int tag;
}tree[maxn<<2][2];
inline void push_down(int rt){
for(int i=0;i<=1;i++){
int l=tree[rt][i].l,r=tree[rt][i].r;
if(tree[rt][i].tag==-1){
continue ;
}
int tmp=tree[rt][i].tag;
tree[ls][i].lx=tree[ls][i].rx=tree[ls][i].mx=tmp*(tree[ls][i].r-tree[ls][i].l+1);
tree[rs][i].lx=tree[rs][i].rx=tree[rs][i].mx=tmp*(tree[rs][i].r-tree[rs][i].l+1);
tree[ls][i].tag=tree[rs][i].tag=tree[rt][i].tag;
tree[rt][i].tag=-1;
}
}
inline void push_up(int rt){
for(int i=0;i<=1;i++){
int l=tree[rt][i].l,r=tree[rt][i].r;
//if(l==r){
//continue;
//} else {
tree[rt][i].lx=tree[ls][i].lx;
tree[rt][i].rx=tree[rs][i].rx;
tree[rt][i].mx=max(max(tree[ls][i].mx,tree[rs][i].mx),tree[ls][i].rx+tree[rs][i].lx);
if(tree[rt][i].lx==tree[ls][i].r-tree[ls][i].l+1){
tree[rt][i].lx+=tree[rs][i].lx;
}
if(tree[rt][i].rx==tree[rs][i].r-tree[rs][i].l+1){
tree[rt][i].rx+=tree[ls][i].rx;
}
// }
}
}
void build(int rt,int l,int r){
for(int i=0;i<=1;i++){
tree[rt][i].l=l;
tree[rt][i].r=r;
tree[rt][i].lx=tree[rt][i].rx=tree[rt][i].mx=r-l+1;
tree[rt][i].tag=-1;
}
if(l==r){
return ;
}
build(lson);
build(rson);
}
void updata(int rt,int pos,int a,int b,int f){
int l=tree[rt][pos].l;
int r=tree[rt][pos].r;
if(l==a&&r==b){
tree[rt][pos].tag=f;
tree[rt][pos].lx=tree[rt][pos].rx=tree[rt][pos].mx=(r-l+1)*f;
return ;
}
push_down(rt);
if(b<=md){
updata(ls,pos,a,b,f);
}else if(a>=md+1){
updata(rs,pos,a,b,f);
}else {
updata(ls,pos,a,md,f);
updata(rs,pos,md+1,b,f);
}
push_up(rt);
}
int query(int rt,int pos,int len){
int l=tree[rt][pos].l,r=tree[rt][pos].r;
if(l==r){
return l;
}
push_down(rt);
if(tree[ls][pos].mx>=len){
return query(ls,pos,len);
}else if(tree[ls][pos].rx>=len-tree[rs][pos].lx){
return tree[ls][pos].r-tree[ls][pos].rx+1;
}else {
return query(rs,pos,len);
}
}
int cas=0;
void solve(){
char ope[10];
int T,N;
scanf("%d %d",&T,&N);
build(1,1,T);
printf("Case %d:\n",++cas);
while(N--){
int pos;
scanf("%s",ope);
//de(ope);
if(ope[0]=='D'){
int tot;scanf("%d",&tot);
if(tree[1][0].mx<tot){
puts("fly with yourself");
}else{
pos=query(1,0,tot);
printf("%d,let's fly\n",pos);
updata(1,0,pos,pos+tot-1,0);
}
}else if(ope[0]=='N'){
int tot;scanf("%d",&tot);
if(tree[1][0].mx<tot&&tree[1][1].mx<tot){
puts("wait for me");
}else{
if(tree[1][0].mx>=tot){
pos=query(1,0,tot);
}else{
pos=query(1,1,tot);
}
printf("%d,don't put my gezi\n",pos);
updata(1,0,pos,pos+tot-1,0);
updata(1,1,pos,pos+tot-1,0);
}
}else{
int a,b;scanf("%d %d",&a,&b);
updata(1,0,a,b,1);
updata(1,1,a,b,1);
puts("I am the hope of chinese chengxuyuan!!");
}
}
}
N题还不会,鸽了
O - 覆盖的面积 HDU - 1255
扫描线模板题,注意这个题要求的是覆盖超过两次的面积,所以需要注意更新len的写法,下面那个题更为简单点
struct LINE {
double x1,x2,h;
int mark;
bool operator < (const LINE &b)const{
return h<b.h;
}
}line[maxn];
struct segtree {
int l,r;
int be;
double len1,len2;
}tree[maxn<<2];
double X[maxn];
void build(int rt,int l,int r){
tree[rt].l=l;
tree[rt].r=r;
tree[rt].be=0;
tree[rt].len1=tree[rt].len2=0.0;
if(l==r){
return ;
} else {
build(lson);
build(rson);
}
}
inline void push_up(int rt){
int l=tree[rt].l,r=tree[rt].r;
if(tree[rt].be){
tree[rt].len1=X[r+1]-X[l];
} else if(l==r){
tree[rt].len1=0;
}else{
tree[rt].len1=tree[ls].len1+tree[rs].len1;
}
if(tree[rt].be>1){
tree[rt].len2=X[r+1]-X[l];
}else if(tree[rt].be==1){
if(l==r){
tree[rt].len2 = 0;
} else {
tree[rt].len2=tree[ls].len1+tree[rs].len1;
}
}else{
if(l == r){
tree[rt].len2 = 0;
} else {
tree[rt].len2 = tree[ls].len2 + tree[rs].len2;
}
}
}
void updata(int rt,const double L,const double R,int f){
int l=tree[rt].l,r=tree[rt].r;
if(X[l]>=R||X[r+1]<=L){
return ;
}
if(X[l]>=L&&X[r+1]<=R){
tree[rt].be+=f;
push_up(rt);
return ;
}
// if(X[l]<=X[md+1]){
updata(ls,L,R,f);
//}
//if(X[r]>=X[md+1]){
updata(rs,L,R,f);
//}
push_up(rt);
}
void solve(){
int n;scanf("%d",&n);
rep(i,1,n){
double xx1,xx2,yy1,yy2;
scanf("%lf %lf %lf %lf",&xx1,&yy1,&xx2,&yy2);
line[i*2-1]={xx1,xx2,yy1,1};
line[i*2]={xx1,xx2,yy2,-1};
X[i*2]=xx1;
X[i*2-1]=xx2;
}
n<<=1;
sort(X+1,X+n+1);
sort(line+1,line+n+1);
int tot = unique(X+1,X+n+1)-X-1;
build(1,1,tot-1);
double ans=0;
rep(i,1,n-1){
updata(1,line[i].x1,line[i].x2,line[i].mark);
ans+=tree[1].len2*(line[i+1].h-line[i].h);
}
printf("%.2f\n",ans);
}
P - Atlantis HDU - 1542
给出每块岛覆盖的地方,求问最后的总面积
这个没啥说的了,就是一个扫描线,(说是二维线段树,按我觉得还是一维的,只维护了线段的长度而已`
struct scanline {
double x1,x2;
double h;
int tag;
bool operator < (const scanline &b) const {
return h < b.h;
}
}arr[maxn];
struct segtree {
int l,r;
double len;
int tag;
}tree[maxn];
double X[maxn];
void build(int rt,int l,int r) {
//de(rt),de(l),de(r);
tree[rt].l = l;
tree[rt].r = r;
tree[rt].tag = 0;
tree[rt].len=0.0;
if(l == r){
return ;
} else {
build (lson);
build (rson);
}
}
inline void push_up(int rt) {
int l = tree[rt].l, r = tree[rt].r;
if(tree[rt].tag){
tree[rt].len=X[r+1]-X[l];
} else {
if(l == r) {
tree[rt].len=0.0;
} else {
tree[rt].len = tree[ls].len + tree[rs].len;
}
}
}
void updata (int rt,double a,double b,int mark) {
int l = tree[rt].l, r = tree[rt].r;
if(X[r+1] <= a || X[l] >= b){
return ;
}
if(X[l] >= a && X[r+1] <= b){
tree[rt].tag+=mark;
push_up(rt);
return ;
}
updata(ls,a,b,mark);
updata(rs,a,b,mark);
push_up(rt);
}
int cas = 0;
void solve() {
int n;while(~scanf("%d",&n)&&n) {
rep (i, 1, n) {
double xx1,xx2,yy1,yy2;
scanf("%lf %lf %lf %lf",&xx1,&yy1,&xx2,&yy2);
arr[i*2-1] = scanline {xx1, xx2, yy1, 1};
arr[i*2] = scanline {xx1, xx2, yy2, -1};
X[i*2] = xx1;
X[i*2-1] = xx2;
}
n <<= 1;
sort(arr+1,arr+n+1);
sort(X+1,X+n+1);
int cnt = unique(X+1,X+n+1)-X-1;
//de(cnt);
build(1,1,cnt-1);
double ans=0.0;
rep(i,1,n-1){
updata(1,arr[i].x1,arr[i].x2,arr[i].tag);
ans+=tree[1].len*(arr[i+1].h-arr[i].h);
}
printf("Test case #%d\n",++cas);
printf("Total explored area: %.2f\n\n",ans);
}
}
最后一题不会嘿嘿完结了