page

page

这道题目在\(luogu\)上有原题--P3419 [POI2005]SAM-Toy Cars

这道题在\(ZR\)时候老师讲过,但是当时讲的是堆做法,比较麻烦,也没有写出来,所以考试的时候写的是权值线段树的做法。

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
const int N=2e5+100;
struct Tree{
	int ls,rs,sum;
}t[N<<1];
deque<int>q[N];
int n,k,ans,cnt=1;
int a[N],tp[N];
bool mark[N];
inline void push_up(int rt)
{
	t[rt].sum=t[t[rt].ls].sum+t[t[rt].rs].sum;
	return ;
}
inline int query(int rt,int l,int r,int k)
{
	if (l==r)
	return l;
	int sz=t[t[rt].ls].sum,mid=(l+r)>>1;
	if (k<=sz) return query(t[rt].ls,l,mid,k);
	else return query(t[rt].rs,mid+1,r,k-sz);
}
inline void update(int rt,int l,int r,int opt,int x)
{
	if (l==r)
	{
		if (opt==1) t[rt].sum++;
		if (opt==2&&t[rt].sum) t[rt].sum--;
		return ;
	}
	int mid=(l+r)>>1;
	if (x<=mid) update(t[rt].ls,l,mid,opt,x);
	else update(t[rt].rs,mid+1,r,opt,x);
	push_up(rt);
	return ;
}
inline void build(int rt,int l,int r)
{
	if (l==r)
	{
		t[rt].sum=0;
		return ;
	}
	int mid=(l+r)>>1;
	t[rt].ls=++cnt;
	build(t[rt].ls,l,mid);
	t[rt].rs=++cnt;
	build(t[rt].rs,mid+1,r);
	return ;
}
int main()
{
	freopen("page.in","r",stdin);
	freopen("page.out","w",stdout);
    int maxx=0;
	scanf("%d%d",&n,&k);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",a+i);
		q[a[i]].push_back(i);
		maxx=max(a[i],maxx);
	}
	build(1,1,n+1);
	for (int i=1;i<=maxx;i++)
	q[i].push_back(n+1);
	for (int i=1;i<=n;i++)
	{
		int x=a[i];
		if (!mark[x])
		{
			if (t[1].sum<k)
			{
				while (q[x].front()<=i)
				q[x].pop_front();
				update(1,1,n+1,1,q[x].front());
				tp[q[x].front()]=x;
				mark[x]=1;
			}
			else
			{
				while (q[x].front()<=i&&!q[x].empty())
				q[x].pop_front();
				int now=query(1,1,n+1,t[1].sum);
				update(1,1,n+1,2,now);
				mark[tp[now]]=0;
				tp[now]=0;
				update(1,1,n+1,1,q[x].front());
				tp[q[x].front()]=x;
				mark[x]=1;
			}
			ans++;
		}
		else
		{
			while (q[x].front()<=i&&!q[x].empty())
			{
				update(1,1,n+1,2,q[x].front());
				tp[q[x].front()]=0;
				q[x].pop_front();
			}
			update(1,1,n+1,1,q[x].front());
			tp[q[x].front()]=x;
		}
	}
	printf("%d\n",ans);
	return 0;
}

考试时犯的错误:

  • 最后一种情况双端队列弹反了
  • 想错数据范围,认为\(a_i<=n\),所以导致预处理错误,以后预处理就按上面的方法了

\(luogu\)题目的代码

#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
const int N=1e5+100,M=5e5+100;
struct Tree{
	int ls,rs,sum;
}t[M<<1];
deque<int>q[N];
int m,n,k,ans,cnt=1;
int a[M],tp[M];
bool mark[M];
inline void push_up(int rt)
{
	t[rt].sum=t[t[rt].ls].sum+t[t[rt].rs].sum;
	return ;
}
inline int query(int rt,int l,int r,int k)
{
	if (l==r) return l;
	int sz=t[t[rt].ls].sum,mid=(l+r)>>1;
	if (k<=sz) return query(t[rt].ls,l,mid,k);
	else return query(t[rt].rs,mid+1,r,k-sz);
}
inline void update(int rt,int l,int r,int opt,int x)
{
	if (l==r)
	{
		if (opt==1) t[rt].sum++;
		if (opt==2&&t[rt].sum) t[rt].sum--;
		return ;
	}
	int mid=(l+r)>>1;
	if (x<=mid) update(t[rt].ls,l,mid,opt,x);
	else update(t[rt].rs,mid+1,r,opt,x);
	push_up(rt);
	return ;
}
inline void build(int rt,int l,int r)
{
	if (l==r)
	{
		t[rt].sum=0;
		return ;
	}
	int mid=(l+r)>>1;
	t[rt].ls=++cnt;
	build(t[rt].ls,l,mid);
	t[rt].rs=++cnt;
	build(t[rt].rs,mid+1,r);
	push_up(rt);
	return ;
}
int main()
{
	int maxx=0;
	scanf("%d%d%d",&m,&k,&n);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",a+i);
		maxx=max(a[i],maxx);
		q[a[i]].push_back(i);
	}
	build(1,1,n+1);
	for (int i=1;i<=maxx;i++)
	q[i].push_back(n+1);
	for (int i=1;i<=n;i++)
	{
		int x=a[i];
		if (!mark[x])
		{
			if (t[1].sum<k)
			{
				while (q[x].front()<=i)
				q[x].pop_front();
				update(1,1,n+1,1,q[x].front());
				tp[q[x].front()]=x;
				mark[x]=1;
			}
			else
			{
				while (q[x].front()<=i)
				q[x].pop_front();
				int now=query(1,1,n+1,t[1].sum);
				update(1,1,n+1,2,now);
				mark[tp[now]]=0;
				tp[now]=0;
				update(1,1,n+1,1,q[x].front());
				tp[q[x].front()]=x;
				mark[x]=1;
			}
			ans++;
		}
		else
		{
			while (q[x].front()<=i)
			{
				update(1,1,n+1,2,q[x].front());
				tp[q[x].front()]=0;
				q[x].pop_front();
			}
			update(1,1,n+1,1,q[x].front());
			tp[q[x].front()]=x;
		}
	}
	printf("%d\n",ans);
	return 0;
}

神奇的multiset+vector写法。

#include<iostream>
#include<cstdio>
#include<vector>
#include<set>
using namespace std;
const int N=1e5+100,M=5e5+100;
int n,k,p,ans;
int a[M],tp[M];
bool mark[N];
vector<int>v[N];
multiset<int>s;
int main()
{
	scanf("%d%d%d",&n,&k,&p);
	int maxx=0;
	for (int i=1;i<=p;i++)
	{
		scanf("%d",a+i);
		v[a[i]].push_back(i);
		maxx=max(a[i],maxx);
	}
	for (int i=1;i<=maxx;i++)
	v[i].push_back(p+1);
	for (int i=1;i<=p;i++)
	{
		int x=a[i];
		if (!mark[x])
		{
			while (v[x][0]<=i)
			v[x].erase(v[x].begin(),v[x].begin()+1);
			if (int(s.size())<k)
			{
				s.insert(v[x][0]);
				tp[v[x][0]]=x;
			}
			else
			{
				set<int>::iterator it=s.end();
				it--;
				mark[tp[*it]]=0;
				tp[*it]=0;
				s.erase(it);
				s.insert(v[x][0]);
				tp[v[x][0]]=x;
			}
			mark[x]=1;
			ans++;
		}
		else
		{
			while (v[x][0]<=i)
			{
				mark[tp[v[x][0]]]=0;
				tp[v[x][0]]=0;
				set<int>::iterator it=s.find(v[x][0]);
				if (it!=s.end()) s.erase(it);
				v[x].erase(v[x].begin(),v[x].begin()+1);
			}
			s.insert(v[x][0]);
			mark[x]=1;
			tp[v[x][0]]=x;
		}
	}
	printf("%d\n",ans);
	return 0;
}

收获:

  • dequequeue不能像vector<int>p[N]这样使用,因为deque这样占用空间很大,\(10^5\)如果这样开需要\(60-70M\)的空间。
  • mutiset.erase(),如果里面是迭代器只会删一个元素,如果是实值会全部删除
  • 平衡树一类的\(STL\),--end()是最大元素,begin()是最小的元素
  • 要分清\(set\)\(multiset\)的特性
posted @ 2019-08-25 15:28  准点的星辰  阅读(400)  评论(0编辑  收藏  举报