8月19日训练总结HDU5023 POJ2777 HDU1540POJ2892POJ3468

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");
            }
        }
    }
}

POJ2777Count Color

和上面那题是一样的,就是输出不一样而已。

#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);
		}
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值