ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

8.5考试总结(NOIP模拟31)[Game·Time·Cover]

2021-08-06 07:02:25  阅读:169  来源: 互联网

标签:insert 8.5 NOIP int 31 mid long ans define


前言

考场上疯狂搞第一题,终于把人给搞没了。。

T1

解题思路

线段树+二分

总体来讲就是用线段树维护三个值:

  1. 没有产生贡献的 a(小 B 的牌)

  2. 没有产生贡献的 b(小 A 的牌)

  3. 产生了的贡献值

对于上面的三个值建一棵权值线段树。

因为要产生贡献,因此,新合并的区间内产生的贡献一定是左区间的 a 右区间的 b 数量的较小值。

然后,对于每一个 a 查找 一个可以配对的最优的 b (如果存在,一定在 a+1 到目前 b 最大值这个区间内)。

如果两者可以产生贡献的话,删去两者之后就会对答案的贡献值减去一

用 muliset 维护剩余的 b 。

因为保证字典序最大,因此在合法区间内查找符合条件的值越大越好。

对于不会产生贡献的 a 二分区间有一些变化,其他的都大同小异了。。

code

#include<bits/stdc++.h>
//#define int long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
#define ls tre[x].l
#define rs tre[x].r
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=1e5+10;
int n,all,sum,root,a[N],b[N];
int cnt,lsh[N<<1];
multiset<int> s;
struct Segment_Tree
{
	int l,r,a,b,dat;
}tre[N<<2];
void push_up(int x)
{
	int tmp=min(tre[rs].b,tre[ls].a);
	tre[x].dat=tre[ls].dat+tre[rs].dat+tmp;
	tre[x].a=tre[ls].a+tre[rs].a-tmp;
	tre[x].b=tre[ls].b+tre[rs].b-tmp;
}
void insert(int &x,int l,int r,int pos,int num1,int num2)
{
	if(!x)	x=++all;
	if(l==r)
	{
		tre[x].a+=num1;
		tre[x].b+=num2;
		return ;
	}
	int mid=(l+r)>>1;
	if(pos<=mid)	insert(ls,l,mid,pos,num1,num2);
	else	insert(rs,mid+1,r,pos,num1,num2);
	push_up(x);
}
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
		lsh[++cnt]=a[i]=read();
	for(int i=1;i<=n;i++)
		lsh[++cnt]=b[i]=read();
	sort(lsh+1,lsh+cnt+1);
	cnt=unique(lsh+1,lsh+cnt+1)-lsh-1;
	for(int i=1;i<=n;i++)
	{
		a[i]=lower_bound(lsh+1,lsh+cnt+1,a[i])-lsh;
		b[i]=lower_bound(lsh+1,lsh+cnt+1,b[i])-lsh;
		insert(root,1,cnt,a[i],1,0);
		insert(root,1,cnt,b[i],0,1);
		s.insert(b[i]);
	}
	sum=tre[root].dat;
	for(int i=1;i<=n;i++)
	{
		insert(root,1,cnt,a[i],-1,0);
		int l=a[i]+1,r=*(--s.end()),ans=-1;
		while(l<=r)
		{
			int mid=(l+r)>>1;
			insert(root,1,cnt,mid,0,-1);
			if(tre[root].dat+1==sum){ans=mid;l=mid+1;}
			else	r=mid-1;
			insert(root,1,cnt,mid,0,1);
		}
		if(ans!=-1)
		{
			sum--;
			insert(root,1,cnt,ans,0,-1);
			s.erase(s.find(ans));
			printf("%d ",lsh[ans]);
			continue;
		}
		l=1;r=a[i];ans=-1;
		while(l<=r)
		{
			int mid=(l+r)>>1;
			insert(root,1,cnt,mid,0,-1);
			if(tre[root].dat==sum){ans=mid;l=mid+1;}
			else	r=mid-1;
			insert(root,1,cnt,mid,0,1);
		}
		insert(root,1,cnt,ans,0,-1);
		s.erase(s.find(ans));
		printf("%d ",lsh[ans]);
	}
	return 0;
}

T2 Time

解题思路

其实就是选择每一种值中不同位置的数到两侧边界距离最小的值进行统计。

相当与是把 swap 当作了一种距离的操作。。

用双端队列或者线段树进行是维护距离最小的。

用树状数组维护到两边的距离(数字个数)就好了。

OMA 运用强悍的卡常技术发现数据里相同的数不会超过 10 个

于是我的内存直接

不得不说手写队列真香

code

#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=1e5+10;
int n,ans,tre[N];
struct Deque
{
	int head,tail,num[15];
	bool empty(){return head>tail;}
	int size(){return tail-head+1;}
	int front(){return num[head];}
	int back(){return num[tail];}
	void clear(){head=1;tail=0;}
	void push(int x){num[++tail]=x;}
	void pop_front(){head++;}
	void pop_back(){tail--;}
};
Deque q;
struct Node
{
	int dat,id;
	bool friend operator < (Node x,Node y)
	{
		if(x.dat==y.dat)	return x.id<y.id;
		return x.dat<y.dat;
	}
}s[N];
int lowbit(int x)
{
	return x&(-x);
}
void add(int x,int num)
{
	for(int i=x;i<=n;i+=lowbit(i))
		tre[i]+=num;
}
int ask(int x)
{
	int temp=0;
	for(int i=x;i;i-=lowbit(i))
		temp+=tre[i];
	return temp;
}
signed main()
{
	n=read();
	for(int i=1;i<=n;i++)
	{
		s[i].dat=read();
		s[i].id=i;
		add(i,1);
	}
	sort(s+1,s+n+1);
	q.clear();
	for(int i=1;i<=n;i++)
	{
		q.push(s[i].id);
		if(s[i].dat==s[i+1].dat)	continue;
		while(!q.empty())
		{
			int x=q.front(),y=q.back();
			int dis1=ask(x-1),dis2=ask(n)-ask(y);
			ans+=min(dis1,dis2);
			if(dis1<dis2)	add(x,-1),q.pop_front();
			else	add(y,-1),q.pop_back();
		}
		q.clear();
	}
	printf("%d",ans);
	return 0;
}

T3 Cover

解题思路

手写队列真不好

快是快了,就是内存有亿点大(3e5*3e5)直接数组越界到飞起。。

因为区间之间仅有包含和不相交的关系,因此可以转化为树形 DP 进行子树合并。

首先对于所有的区间以左端点为第一关键字,右端点为第二关键字进行排序。

然后用一个栈进行维护就可以完成建边的操作了。

树形 DP 的值用 multiset 维护。

进行子树合并的事后,每次取 set 里最大的值进行合并,保证取某一层里最大的值。

然后就没啥了,思路以及打法确实很妙

code

#include<bits/stdc++.h>
#define int long long
#define ll long long
#define ull unsigned long long
#define f() cout<<"Pass"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch>'9'||ch<'0')
	{
		if(ch=='-')	f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*f;
}
const int N=3e5+10;
int tot,head[N],ver[N],nxt[N];
int n,sta[N],top,ans;
void add(int x,int y)
{
	ver[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
}
struct Node
{
	int l,r,dat;
	void insert(){l=read();r=read();dat=read();}
	bool friend operator < (Node x,Node y)
	{
		if(x.l==y.l)	return x.r>y.r;
		return x.l<y.l;
	}
}s[N];
multiset<int> f[N];
void merge(multiset<int> &x,multiset<int> &y)
{
	if(x.size()<y.size())	swap(x,y);
	queue<int> q;
	for(auto it=y.begin();it!=y.end();it++)
	{
		q.push(*it+*x.begin());
		x.erase(x.begin());
	}
	while(!q.empty())
	{
		x.insert(q.front());
		q.pop();
	}
}
void dfs(int x)
{
	for(int i=head[x];i;i=nxt[i])
	{
		int to=ver[i];
		dfs(to);
		merge(f[x],f[to]);
	}
	f[x].insert(-s[x].dat);
}
signed main()
{
	n=read();n=read();
	for(int i=1;i<=n;i++)
		s[i].insert();
	sort(s+1,s+n+1);
	sta[++top]=0;
	for(int i=1;i<=n;i++)
	{
		while(sta[top]&&s[sta[top]].r<s[i].r)	top--;
		add(sta[top],i);
		sta[++top]=i;
	}
	dfs(0);
	for(int i=1;i<=n;i++)
	{
		if(f[0].size())
		{
			ans-=*f[0].begin();
			f[0].erase(f[0].begin());
		}
		printf("%lld ",ans);
	}
	return 0;
}

标签:insert,8.5,NOIP,int,31,mid,long,ans,define
来源: https://www.cnblogs.com/Varuxn/p/15105361.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有