HDU5023A Corrupt Mayor's Performance Art
区间染色,然后区间查询出现的所有颜色。
线段树区间覆盖不用多说,主要是对于颜色如何查重。每一个区间加个set肯定不行,写着太麻烦,最后需要迭代器输出,而且向上转移的时候也需要迭代器循环放入,很可能会超时。
注意到只有30种颜色,联想到状压dp,可以用进制位表示该颜色是否被使用。第一个颜色就是1,第二个颜色就是2……第n个颜色就是1<<(n-1),这样合并的时候使用“|”运算符即可(有1为1,全0为0)。对于最后表示结果的数,二进制表示下第i位出现1代表第i个颜色被使用了。
不过一开始1e5的线段树做多了,下意识地定义1e5。不负众望地RE了。。。
#include <cstdio>
#include <algorithm>
#define LL long long
#define rc d<<1|1
#define lc d<<1
using namespace std;
const int N=1e6+5;
struct node
{
int l,r,c,lazy;//lazy表示当前区域都是第几种颜色
}tr[N<<2];
void build(int d,int l,int r)
{
tr[d].l=l;
tr[d].r=r;
tr[d].c=1<<1;
tr[d].lazy=-1;
if(l==r)
return ;
int mid=(tr[d].l+tr[d].r)>>1;
build(lc,l,mid);
build(rc,mid+1,r);
}
void pushdown(int d)
{
if(tr[d].lazy!=-1)
{
tr[lc].c=tr[rc].c=1<<tr[d].lazy;
tr[lc].lazy=tr[rc].lazy=tr[d].lazy;
tr[d].lazy=-1;
}
}
void update(int d,int L,int R,int c)
{
if(tr[d].l==L&&tr[d].r==R)
{
tr[d].lazy=c;
tr[d].c=1<<tr[d].lazy;
return ;
}
pushdown(d);
int mid=(tr[d].l+tr[d].r)>>1;
if(mid>=R) update(lc,L,R,c);
else if(L>mid) update(rc,L,R,c);
else update(lc,L,mid,c),update(rc,mid+1,R,c);
tr[d].c=tr[lc].c|tr[rc].c;//合并颜色
}
int query(int d,int L,int R)
{
if(tr[d].l==L&&tr[d].r==R)
return tr[d].c;
pushdown(d);
int mid=(tr[d].l+tr[d].r)>>1;
if(mid>=R) return query(lc,L,R);
else if(L>mid) return query(rc,L,R);
else return query(lc,L,mid)|query(rc,mid+1,R);
}
int main()
{
int l,o;
while(scanf("%d%d%d",&l,&o)&&l)
{
build(1,1,l);
char s[2];
int a,b,c;
for(int i=0;i<o;i++)
{
scanf("%s",s);
if(s[0]=='P')
{
scanf("%d%d%d",&a,&b,&c);
update(1,a,b,c-1);
}
else
{
scanf("%d%d",&a,&b);
int ans=query(1,a,b),cnt=0;
while(ans)
{
cnt++;
if(ans&1==1)
{
if(ans>>1!=0)
printf("%d ",cnt);
else
printf("%d",cnt);
}
ans>>=1;
}
printf("\n");
}
}
}
}
和上面那题是一样的,就是输出不一样而已。
#include <cstdio>
#include <algorithm>
#define LL long long
#define rc d<<1|1
#define lc d<<1
using namespace std;
const int N=2e5+5;
struct node
{
int l,r,c,lazy;
}tr[N<<2];
void build(int d,int l,int r)
{
tr[d].l=l;
tr[d].r=r;
tr[d].c=1;
tr[d].lazy=-1;
if(l==r)
return ;
int mid=(tr[d].l+tr[d].r)>>1;
build(lc,l,mid);
build(rc,mid+1,r);
}
void pushdown(int d)
{
if(tr[d].lazy!=-1)
{
tr[lc].c=tr[rc].c=1<<tr[d].lazy;
tr[lc].lazy=tr[rc].lazy=tr[d].lazy;
tr[d].lazy=-1;
}
}
void update(int d,int L,int R,int c)
{
if(tr[d].l==L&&tr[d].r==R)
{
tr[d].lazy=c;
tr[d].c=1<<tr[d].lazy;
return ;
}
pushdown(d);
int mid=(tr[d].l+tr[d].r)>>1;
if(mid>=R) update(lc,L,R,c);
else if(L>mid) update(rc,L,R,c);
else update(lc,L,mid,c),update(rc,mid+1,R,c);
tr[d].c=tr[lc].c|tr[rc].c;
}
int query(int d,int L,int R)
{
if(tr[d].l==L&&tr[d].r==R)
return tr[d].c;
pushdown(d);
int mid=(tr[d].l+tr[d].r)>>1;
if(mid>=R) return query(lc,L,R);
else if(L>mid) return query(rc,L,R);
else return query(lc,L,mid)|query(rc,mid+1,R);
}
int main()
{
int l,t,o;
while(~scanf("%d%d%d",&l,&t,&o))
{
build(1,1,l);
char s[2];
int a,b,c;
for(int i=0;i<o;i++)
{
scanf("%s",s);
if(s[0]=='C')
{
scanf("%d%d%d",&a,&b,&c);
update(1,min(a,b),max(a,b),c-1);
}
else
{
scanf("%d%d",&a,&b);
int ans=query(1,min(a,b),max(a,b)),cnt=0;
while(ans)
{
if(ans&1==1)
cnt++;
ans>>=1;
}
printf("%d\n",cnt);
}
}
}
}
HDU1540Tunnel Warfare/POJ2892Tunnel Warfare
这是一个题。
线段树区间和并的经典题。单点修改,找某一位所在的最长连续序列。
就是设置一个左边连续长度,右边连续长度,以及区间最长度。这个区间最长度可以一开始写完觉得用处不大,个人认为只是为了方便后面剪枝。。。
查询的地方我大于小于号我调了好久。。。
#include <cstdio>
#include <algorithm>
#include <stack>
#define lc d<<1
#define rc d<<1|1
using namespace std;
const int N=5e4+5;
struct node
{
int l,r,ls,rs,ms;//ls左边连续长度,rs右边连续长度,ms区间最长长度
}tr[N<<2];
void build(int d,int l,int r)
{
tr[d].l=l;
tr[d].r=r;
tr[d].ls=tr[d].rs=tr[d].ms=r-l+1;//一开始都是连续的
if(l==r)
return ;
int mid=(l+r)>>1;
build(lc,l,mid);
build(rc,mid+1,r);
}
void update(int d,int p,int val)
{
if(tr[d].l==tr[d].r)
{
tr[d].ls=tr[d].ms=tr[d].rs=val;
return ;
}
int mid=(tr[d].l+tr[d].r)>>1;
if(p<=mid) update(lc,p,val);
else update(rc,p,val);
tr[d].ms=max(tr[lc].ms,tr[rc].ms);//开始更新操作
tr[d].ms=max(tr[lc].rs+tr[rc].ls,tr[d].ms);//两个子块相邻的边连续起来可能成为新的最大
tr[d].ls=tr[lc].ls;
tr[d].rs=tr[rc].rs;
if(tr[lc].ls==tr[lc].r-tr[lc].l+1)//如果左儿子全是连续的,就要加上右儿子的左连续
tr[d].ls+=tr[rc].ls;
if(tr[rc].rs==tr[rc].r-tr[rc].l+1)//同理
tr[d].rs+=tr[lc].rs;
}
int query(int d,int p)
{
if(tr[d].l==tr[d].r||tr[d].r-tr[d].l+1==tr[d].ms||tr[d].ms==0)//这里就显示出了ms的作用
return tr[d].ms;
int mid=(tr[d].l+tr[d].r)>>1;
if(p<=mid)//这一块的大于小于号我调了很长时间
{
if(p>=tr[lc].r-tr[lc].rs+1)
return tr[lc].rs+tr[rc].ls;
return query(lc,p);
}
else
{
if(p<=tr[rc].l+tr[rc].ls-1)
return tr[rc].ls+tr[lc].rs;
return query(rc,p);
}
}
int main()
{
int n,m;
stack<int>s;
while(~scanf("%d%d",&n,&m))
{
while(!s.empty())
s.pop();
build(1,1,n);
char ord[2];
int x;
for(int i=0;i<m;i++)
{
scanf("%s",ord);
if(ord[0]=='D')
{
scanf("%d",&x);
s.push(x);
update(1,x,0);
}
else if(ord[0]=='Q')
{
scanf("%d",&x);
printf("%d\n",query(1,x));
}
else
{
update(1,s.top(),1);
s.pop();
}
}
}
}
POJ3468 A Simple Problem with Integers
线段树基本例题,区间修改,区间查询。
我竟然WA了好几发,我好菜啊。。。
#include <cstdio>
#define LL long long
#define lc d<<1
#define rc d<<1|1
using namespace std;
const int N=1e5+5;
struct node
{
int l,r;
LL lazy;//lazy也要开LL
LL sum;
}tr[N<<4];
LL num[N];
void build(int d,int l,int r)
{
tr[d].l=l;
tr[d].r=r;
tr[d].lazy=0;
int mid=(tr[d].l+tr[d].r)>>1;
if(l==r)
tr[d].sum=num[l];
else
{
build(lc,l,mid);
build(rc,mid+1,r);
tr[d].sum=tr[lc].sum+tr[rc].sum;
}
}
void pushdown(int d)
{
if(tr[d].lazy!=0)
{
tr[lc].lazy+=tr[d].lazy;
tr[rc].lazy+=tr[d].lazy;
tr[lc].sum+=(tr[lc].r-tr[lc].l+1)*tr[d].lazy;
tr[rc].sum+=(tr[rc].r-tr[rc].l+1)*tr[d].lazy;
tr[d].lazy=0;
// tr[d].lazy=0; 这样是错的,因为是每次加上新加的值tr[d].lazy,
// tr[lc].sum+=(tr[lc].r-tr[lc].l+1)*tr[lc].lazy; 而不是每次加上所有的值tr[lc].lazy,如果一开始tr[lc].lazy!=0,
// tr[rc].sum+=(tr[rc].r-tr[rc].l+1)*tr[rc].lazy; 就重复增加了
}
}
void update(int d,int L,int R,LL x)
{
if(tr[d].l==L&&tr[d].r==R)
{
tr[d].sum+=x*(R-L+1);
tr[d].lazy+=x;
return ;
}
pushdown(d);
int mid=(tr[d].l+tr[d].r)>>1;
if(R<=mid) update(lc,L,R,x);
else if(L>mid) update(rc,L,R,x);
else update(lc,L,mid,x),update(rc,mid+1,R,x);
tr[d].sum=tr[lc].sum+tr[rc].sum;
}
LL query(int d,int L,int R)
{
if(tr[d].l==L&&tr[d].r==R)
return tr[d].sum;
pushdown(d);
int mid=(tr[d].l+tr[d].r)>>1;
if(R<=mid) return query(lc,L,R);
else if(L>mid) return query(rc,L,R);
else return query(lc,L,mid)+query(rc,mid+1,R);
}
int main()
{
int n,q;
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++)
scanf("%lld",&num[i]);
build(1,1,n);
char s[2];
int a,b;
LL c;
for(int i=0;i<q;i++)
{
scanf("%s",s);
if(s[0]=='Q')
{
scanf("%d%d",&a,&b);
printf("%lld\n",query(1,a,b));
}
else
{
scanf("%d%d%lld",&a,&b,&c);
update(1,a,b,c);
}
}
}