NOIP模拟22「d·e·f」

T1:d

  枚举。
  现在都不敢随便打枚举了。
  实际上我们只关注最后留下的矩阵中最小的长与宽即可。
  所以我们将所有矩阵按a的降序排列。
  从第\(n-m\)个开始枚举。
  因为你最多拿走\(m\)个。
  考虑到交面积是越交越小的,所以我们尽可能的多拿矩阵走。
  我们将前\(n-m\)个矩阵丢进堆里,按b排序,小根堆。
  我们只关心b第\(n-m\)大的矩阵,因为我们已经决定要刘m个了,原因已经说过了。
  所以我们要让堆顶尽可能大。并维护堆的大小为\(n-m\)即可。具体实现见代码。
  1A代码。。我爱战神。。。。

#include<bits/stdc++.h>
using namespace std;
namespace STD
{
	#define ll long long
	#define rr register 
	#define inf INT_MAX
	const int N=1e5+4;
	int n,m,t;
	struct mat
	{
		ll a,b;
		bool operator<(const mat b_) const
		{
			return a>b_.a;
		}
		mat(){}
		mat(int a_,int b_){a=a_,b=b_;}
	}x[N];
	int read()
	{
		rr int x_read=0,y_read=1;
		rr char c_read=getchar();
		while(c_read<'0'||c_read>'9')
		{
			if(c_read=='-') y_read=-1;
			c_read=getchar();
		}
		while(c_read<='9'&&c_read>='0')
		{
			x_read=(x_read*10)+(c_read^48);
			c_read=getchar();
		}
		return x_read*y_read;
	}
	class Heap
	{
		private:
			int tot;
			mat heap[N<<2];
			void push_up(int x)
			{
				while(x>1)
				{
					if(heap[x].b<heap[x>>1].b)
					{
						swap(heap[x],heap[x>>1]);
						x>>=1;
					}
					else break;
				}
			}
			void push_down(int x)
			{
				int s=x<<1;
				while(s<=tot)
				{
					if(s<tot&&heap[s].b>heap[s+1].b) s++;
					if(heap[s].b<heap[x].b)
					{
						swap(heap[x],heap[s]);
						x=s,s=x<<1;
					}
					else break;
				}
			}
		public:
			Heap(){tot=0;}
			void reset()
			{
				for(rr int i=1;i<=tot;i++)
					heap[i]=mat(0,0);
				tot=0;
			}
			void insert(mat x)
			{
				heap[++tot]=x;
				push_up(tot);
			}
			mat top(){return heap[1];}
			void pop()
			{
				heap[1]=heap[tot--];
				push_down(1);
			}
			void remove(int x)
			{
				heap[x]=heap[tot--];
				push_up(x);
				push_down(x);
			}
			int size(){return tot;}
	}heap;
};
using namespace STD;
int main()
{
	t=read();
	while(t--)
	{
		n=read(),m=read();
		for(rr int i=1;i<=n;i++)
			x[i].a=read(),x[i].b=read();
		sort(x+1,x+1+n);
		for(rr int i=1;i<=n-m;i++)
			heap.insert(x[i]);
		ll ans=-inf;
		ans=max(ans,heap.top().b*x[n-m].a);
		for(rr int i=n-m+1;i<=n;i++)
		{
			if(x[i].b>heap.top().b)
			{
				heap.pop();
				heap.insert(x[i]);
			}
			ans=max(ans,x[i].a*heap.top().b);
		}
		printf("%lld\n",ans);
		heap.reset();
	}	
}

  战神跟我讲的只关心最后留谁这个思想我考试想到了。但当时只是骗分,用这个思想打了个状压就走了。没细想。
  这道题写的骗分都对了,开心。QWQ

T2:e

  考试时想出了这个联通块其实就是从\(LCA\)到各个点,打了个树剖就走了,暴力跳得父亲。
  本机跑进了1s,用的90000多的数据,还挺开心,以为稳了。结果\(T\)了,笑死。
  正解是还要打一个主席树,以节点为版本,记录从自己到根的权值情况。
  AC代码:

#include<bits/stdc++.h>
using namespace std;
namespace STD
{
	#define ll long long
	#define rr register
	#define inf INT_MAX
	const int N=1e5+6;
	int n,q,type,cnt;
	int a[N],x[N],a_[N];
	int to[N<<1],dire[N<<1],head[N];
	inline void add(int f,int t)
	{
		static int num1=0;
		to[++num1]=t;
		dire[num1]=head[f];
		head[f]=num1;
	}
	int read()
	{
		rr int x_read=0,y_read=1;
		rr char c_read=getchar();
		while(c_read<'0'||c_read>'9')
		{
			if(c_read=='-') y_read=-1;
			c_read=getchar();
		}
		while(c_read<='9'&&c_read>='0')
		{
			x_read=(x_read*10)+(c_read^48);
			c_read=getchar();
		}
		return x_read*y_read;
	}
	int fa[N],son[N],top[N],size[N],depth[N];
	void dfs1(int x)
	{
		size[x]=1;
		for(rr int i=head[x];i;i=dire[i])
		{
			if(to[i]==fa[x]) continue;
			fa[to[i]]=x;
			depth[to[i]]=depth[x]+1;
			dfs1(to[i]);
			size[x]+=size[to[i]];
			if(size[to[i]]>size[son[x]]) son[x]=to[i];
		}
	}
	void dfs2(int x)
	{
		if(x==son[fa[x]]) top[x]=top[fa[x]];
		else top[x]=x;
		for(rr int i=head[x];i;i=dire[i])
		{
			if(to[i]==fa[x]) continue;
			dfs2(to[i]);
		}
	}
	int LCA(int x,int y)
	{
		while(top[x]!=top[y])
		{
			if(depth[top[x]]>depth[top[y]])
				x=fa[top[x]];
			else y=fa[top[y]];
		}
		return depth[x]<depth[y]?x:y;
	}
	class Pst
	{
		private:
			int tot;
			int root[N];
			int lc[(N<<1)+N*20];
			int rc[(N<<1)+N*20];
			int sum[(N<<1)+N*20];
			void Insert(int before,int &now,int l,int r,int val);
			void Build(int &now,int l,int r);
			int query_sum(int before,int now,int l,int r,int st,int en);
			int query_rank(int before,int now,int l,int r,int rank);
		public:
			void build(){Build(root[0],1,n+2);}
			void insert(int fa,int son,int val){Insert(root[fa],root[son],1,n+2,val);}
			int prev(int fa,int son,int val)
			{
				int rank=query_sum(root[fa],root[son],1,n+2,1,val);
				return query_rank(root[fa],root[son],1,n+2,rank);
			}
			int succ(int fa,int son,int val)
			{
				int rank=query_sum(root[fa],root[son],1,n+2,1,val);	
				return query_rank(root[fa],root[son],1,n+2,rank+1);
			}
	}t;
	void Pst::Build(int &now,int l,int r)
	{
		now=++tot;
		if(l==r) return;
		int mid=(l+r)>>1;
		Build(lc[now],l,mid),Build(rc[now],mid+1,r);
	}
	void Pst::Insert(int before,int &now,int l,int r,int val)
	{
		now=++tot;
		sum[now]=sum[before];
		if(l==r){sum[now]++;return;}
		int mid=(l+r)>>1;
		if(val<=mid)
		{
			rc[now]=rc[before];
			Insert(lc[before],lc[now],l,mid,val);
		}
		else
		{
			lc[now]=lc[before];
			Insert(rc[before],rc[now],mid+1,r,val);
		}
		sum[now]=sum[lc[now]]+sum[rc[now]];
	}
	int Pst::query_sum(int before,int now,int l,int r,int st,int en)
	{
		if(st<=l&&r<=en) return sum[now]-sum[before];
		int mid=(l+r)>>1;
		int ret=0;
		if(st<=mid) ret+=query_sum(lc[before],lc[now],l,mid,st,en);
		if(mid<en) ret+=query_sum(rc[before],rc[now],mid+1,r,st,en);
		return ret;
	}
	int Pst::query_rank(int before,int now,int l,int r,int rank)
	{
		if(l==r) 
		{
			if(sum[now]-sum[before])
				return l;
			return inf;
		}
		int mid=(l+r)>>1;
		int num=(sum[lc[now]]-sum[lc[before]]);
		if(num>=rank)
			return query_rank(lc[before],lc[now],l,mid,rank);
		return query_rank(rc[before],rc[now],mid+1,r,rank-num);
	}
	void dfs3(int x)
	{
		t.insert(fa[x],x,a[x]);
		for(rr int i=head[x];i;i=dire[i])
		{
			if(to[i]==fa[x]) continue;
			dfs3(to[i]);
		}
	}
	int find(int x)
	{
		int l=1,r=cnt;
		while(l<r)
		{
			int mid=(l+r+1)>>1;
			if(a_[mid]<=x) l=mid;
			else r=mid-1;
		}
		return l;
	}
};
using namespace STD;
int main()
{
	n=read(),q=read(),type=read();
	for(rr int i=1;i<=n;i++) a[i]=a_[i]=read();
	for(rr int i=1;i<n;i++)
	{
		int u=read(),v=read();
		add(u,v),add(v,u);
	}
	sort(a_+1,a_+1+n);
	cnt=unique(a_+1,a_+1+n)-a_-1;
	for(rr int i=1;i<=n;i++)
		a[i]=lower_bound(a_+1,a_+1+cnt,a[i])-a_;
	t.build();
	dfs1(1);
	dfs2(1);
	dfs3(1);
	int lastans=0;
	while(q--)
	{
		int r=read(),k=read();
		for(rr int i=1;i<=k;i++)
		{
			x[i]=read();
			x[i]=(x[i]-1+lastans*type)%n+1;
		}
		int lca=x[1];
		int r_=find(r);
		for(rr int i=2;i<=k;i++)
			lca=LCA(lca,x[i]);
		int ans=inf;
		for(rr int i=1;i<=k;i++)
		{
			int prev=t.prev(fa[lca],x[i],r_);
			int succ=t.succ(fa[lca],x[i],r_);
			if(prev!=inf)
				ans=min(ans,abs(a_[prev]-r));
			if(succ!=inf)
				ans=min(ans,abs(a_[succ]-r));
		}
		lastans=ans;
		printf("%d\n",ans);
		for(rr int i=1;i<=k;i++)
			x[i]=0;
	}
}

  这道题还涉及到了主席树加减,我直接懵逼了。。。。。。
  跑回去学了一下,A了道板子题。
  改题时二分写错了,改了半天,这道题细节蛮多的,下面我会提到我改题时碰到的几个细节。

1.二分,我原本用的lower_bound,STL库里的。他的返回值是“在保证有序性的情况下这个数据应该被插在哪”
我为什么会提到这个呢??因为我一开始给r离散化,用的这个,但他的返回是大于或等于r的值得位置,我主席树里是要用小于或等于r的值的位置。所以WA了
后来改为手写,用的李煜东的那本书(算法竞赛进阶指南)上给的算法,才过的。
要看着本书的要注意,打二分一定严格按照标程,他给出的两种二分写法是严格与求前趋后继向配套的,是不可混用的我混用了,然后调了半天FWF。。。
2.联通块是包括lca的,所以在查前趋后继时要用fa[lca]与x[i]的版本,而不是lca与x[i]的版本。
3.主席树查前趋后继的时候要注意处理没有前趋后继的情况。

  大概就这些吧。

T3:f

  还在调,先鸽掉。

posted @ 2021-07-22 11:48  Geek_kay  阅读(36)  评论(0编辑  收藏  举报