洛谷P1047 [NOIP2005 普及组] 校门外的树 题解

前言

如何把一道入门题写成省选题?(手动滑稽)

本题解是我在练习分块时突发奇想写的,真就把入门题写成省选题的感觉(

才发现原来这些简单题这么有趣(



P1047 [NOIP2005 普及组] 校门外的树 题解

题目链接: P1047 [NOIP2005 普及组] 校门外的树

题意:马路上砍树,问砍了 m m m 次还有几棵树

一、模拟解法(正常解法)

我们只要把每次砍掉的树标记一下,最后统计未标记的数量即可

注意下标从 0 0 0 开始,共 n + 1 n+1 n+1 个数

代码如下:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define R register
#define MAXN (int)(1e4+5)
int n,m,ans;
bool a[MAXN];
signed main()
{
	scanf("%lld%lld",&n,&m);
	for(R int i=1,l,r; i<=m; i++)
	{
		scanf("%lld%lld",&l,&r);
		for(R int j=l; j<=r; j++)
			a[j]=1;
	}
	for(R int i=0; i<=n; i++)ans+=(a[i]==0);
	printf("%lld\n",ans);
	return 0;
}

二、线段树解法(开始奇怪起来)

直接用线段树维护区间,没什么特别的

代码如下:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define R register
#define MAXN (int)(1e4+5)
template<typename T>inline void read(R T &k)
{
	R char ch=getchar(); R T x=0,f=1;
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	k=x*f;
}
int n,m,a[MAXN],ans[MAXN<<2],tag[MAXN<<2];
inline int ls(R int x){return x<<1;}
inline int rs(R int x){return x<<1|1;}
void proc(R int l,R int r,R int at)
{
	R int u=at>>1;
	if(tag[u])ans[at]=0,tag[at]=1;
}
void push_up(R int at){ans[at]=ans[ls(at)]+ans[rs(at)];}
void push_down(R int l,R int r,R int at)
{
	R int mid=(l+r)>>1;
	proc(l,mid,ls(at));
	proc(mid+1,r,rs(at));
	tag[at]=0;
}
void build(R int l,R int r,R int at)
{
	tag[at]=0;
	if(l==r){ans[at]=a[l];return;}
	R int mid=(l+r)>>1;
	build(l,mid,ls(at));
	build(mid+1,r,rs(at));
	push_up(at);
}
void update(R int nl,R int nr,R int l,R int r,R int at)
{
	if(nl<=l&&r<=nr)
	{
		ans[at]=0;
		tag[at]=1;
		return;
	}
	R int mid=(l+r)>>1;
	push_down(l,r,at);
	if(nl<=mid)update(nl,nr,l,mid,ls(at));
	if(nr>mid)update(nl,nr,mid+1,r,rs(at));
	push_up(at);
}
int query(R int nl,R int nr,R int l,R int r,R int at)
{
	R int res=0;
	if(nl<=l&&r<=nr)
		return ans[at];
	R int mid=(l+r)>>1;
	push_down(l,r,at);
	if(nl<=mid)res+=query(nl,nr,l,mid,ls(at));
	if(nr>mid)res+=query(nl,nr,mid+1,r,rs(at));
	return res;
}
signed main()
{
	read(n);read(m);++n;
	for(R int i=1; i<=n; i++)
		a[i]=1;
	build(1,n,1);
	while(m--)
	{
		R int l,r;
		read(l);read(r);
		update(l+1,r+1,1,n,1);
	}
	printf("%lld\n",query(1,n,1,n,1));
	return 0;
}

三、分块解法 (开始毒瘤起来)

只有一个要注意的,就是每次暴力减的时候要防止减为负数
代码如下:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define R register
#define MAXN (int)(1e4+5)
template<typename T>inline void read(R T &k)
{
	R char ch=getchar(); R T x=0,f=1;
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	k=x*f;
}
int n,m,len;
int a[MAXN],ans[MAXN],id[MAXN],tag[MAXN];
void add(R int l,R int r)
{
	R int lid=id[l],rid=id[r];
    if(lid==rid)
    {
        for(R int i=l; i<=r; i++)
            ans[id[i]]-=a[i],a[i]=0;
        return;
    }
    for(R int i=l; i<=lid*len&&!tag[lid]; i++)
        ans[id[i]]-=a[i],a[i]=0;
    for(R int i=lid+1;i<rid; i++)
        tag[i]=1,ans[i]=0;
    for(R int i=(rid-1)*len+1; i<=r&&!tag[rid]; i++)
        ans[id[i]]-=a[i],a[i]=0;
}
int sum(R int l,R int r)
{
    R int res=0;
    R int lid=id[l],rid=id[r];
    if(tag[lid])
    {
        for(R int i=(lid-1)*len+1; i<=len*lid; i++)
            ans[id[i]]-=a[i],a[i]=0;
    }
    if(tag[rid])
    {
        for(R int i=(rid-1)*len+1; i<=len*rid; i++)
            ans[id[i]]-=a[i],a[i]=0;
    }
    if(lid==rid)
    {
        for(R int i=l; i<=r; i++)
            res+=a[i];
        return res;
    }
    for(R int i=l; i<=lid*len; i++)
        res+=a[i];
    for(R int i=lid+1; i<rid; i++)
        res+=ans[i];
    for(R int i=(rid-1)*len+1; i<=r; i++)
        res+=a[i];
    return res;
}
signed main()
{
	read(n);read(m);++n;len=sqrt(n);
	for(R int i=1; i<=n; i++)
	{
		a[i]=1;
		id[i]=(i-1)/len+1;
		ans[id[i]]++;
	}
	while(m--)
	{
		R int x,y;
		read(x);read(y);
		add(x+1,y+1);
	}
	printf("%lld\n",sum(1,n));
	return 0;
}

四、珂朵莉树解法 (非常珂学)

一看这个推平操作,立马就想到了珂朵莉树

如果不知道珂朵莉树的可以看看我写的这篇文章

代码如下:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define R register
#define MAXN (int)(1e4+5)
template<typename T>inline void read(R T &k)
{
	R char ch=getchar(); R T x=0,f=1;
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	k=x*f;
}
int n,m;
int a[MAXN];
struct node
{
	int l,r;
	mutable int v;
	bool operator<(const node &o)const
		{return l<o.l;}
};
set<node>s;
set<node>::iterator split(R int pos)
{
	set<node>::iterator it=s.lower_bound({pos});
	if(it!=s.end()&&it->l==pos)return it;
	--it;
	if(it->r<pos)return s.end();
	R int l=it->l,r=it->r,v=it->v;
	s.erase(it);
	s.insert({l,pos-1,v});
	return s.insert({pos,r,v}).first;
}
void assign(R int l,R int r)
{
	set<node>::iterator itr=split(r+1),itl=split(l);
	s.erase(itl,itr);
	s.insert({l,r,0});
}
int sum(R int l,R int r)
{
	R int res=0;
	set<node>::iterator itr=split(r+1),itl=split(l);
	for(set<node>::iterator it=itl; it!=itr; it++)
		res+=it->v*(it->r-it->l+1);
	return res;
}
signed main()
{
	read(n);read(m);++n;
	s.insert({1,n,1});
	while(m--)
	{
		R int x,y;
		read(x);read(y);
		assign(x+1,y+1);
	}
	printf("%lld\n",sum(1,n));
	return 0;
}

总结

本文简单介绍了几种解法来解决此题

转载请说明出处


题外话

数据太水了,所以这几种做法的速度都很快

附上提交记录叭…

模拟解法 线段树解法 分块解法 珂朵莉树解法

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值