(联考)noip84

T1

将宝藏按权值从小到大排序,答案必定单调不增,所以可以拿个指针从后往前扫。

判断当前选的是否合法,显然是从权值比它大的和从权值比它小的里各选 \(x\) 个时间最小的,区间前 \(k\) 小的值的和可以通过线段树二分解决。

Code
#include<cstdio>
#include<cctype>
#include<algorithm>
#define re register
#define long long long
using std::sort;
using std::unique;
using std::lower_bound;
const int MAX = 3e5+3;
#define scanf oma=scanf
#define freopen file=freopen
int oma;
FILE* file;
namespace some
{
	struct stream
	{
		template<typename type>inline stream &operator >>(type &s)
		{
			bool w=0; s=0; char ch=getchar();
			while(!isdigit(ch)){ w|=ch=='-'; ch=getchar(); }
			while(isdigit(ch)){ s=(s<<1)+(s<<3)+(ch^48); ch=getchar(); }
			return s=w?-s:s,*this;
		}
	}cin;
	long t;
	int n,q;
	struct my
	{
		int w,t;
		inline friend bool operator <(const my &a,const my &b)
		{ return a.w<b.w;; }
	}val[MAX];
	int ans[MAX],tmp[MAX];
	auto min = [](int a,int b) { return a<b?a:b; };
	#define debug(s) printf("%s\n",s)
}using namespace some;
namespace Segment_TREE
{
	struct Segment_Tree
	{
		struct TREE
		{
			int cnt;
			long sum;
		}st[MAX<<2];
		#define ls(p) p<<1
		#define rs(p) p<<1|1
		#define mid (l+r>>1)
		void Push_up(int p)
		{
			st[p].cnt = st[ls(p)].cnt+st[rs(p)].cnt;
			st[p].sum = st[ls(p)].sum+st[rs(p)].sum;
		}
		void insert(int p,int l,int r,int pos,int w)
		{
			if(l==r)
			{ st[p].cnt += w,st[p].sum += tmp[l]*w; return ; }
			if(pos<=mid)
			{ insert(ls(p),l,mid,pos,w); }
			else
			{ insert(rs(p),mid+1,r,pos,w); }
			Push_up(p);
		}
		int cnt;
		long sum;
		void query(int p,int l,int r)
		{
			if(!cnt)
			{ return ; }
			//printf("%d %d %d %d %d\n",p,l,r,cnt,st[p].cnt);
			if(l==r)
			{ sum += 1ll*min(st[p].cnt,cnt)*tmp[l],cnt -= min(cnt,st[p].cnt); return ; }
			if(st[ls(p)].cnt>cnt)
			{ query(ls(p),l,mid); }
			else if(st[ls(p)].cnt==cnt)
			{ cnt = 0,sum += st[ls(p)].sum; }
			else if(st[ls(p)].cnt<cnt)
			{ cnt -= st[ls(p)].cnt,sum += st[ls(p)].sum; query(rs(p),mid+1,r); }
		}
	}up,down;
}using namespace Segment_TREE;
namespace OMA
{
	auto main = []() -> signed
	{
		//freopen("node.in","r",stdin); //freopen("my.out","w",stdout);
		freopen("treasure.in","r",stdin); freopen("treasure.out","w",stdout);
		cin >> n >> t >> q;
		for(re int i=1; i<=n; i++)
		{ cin >> val[i].w >> val[i].t; tmp[i] = val[i].t; }
		sort(val+1,val+1+n),sort(tmp+1,tmp+1+n);
		int tot = unique(tmp+1,tmp+1+n)-tmp-1,p = n;
		val[n].t = lower_bound(tmp+1,tmp+1+tot,val[n].t)-tmp;
		for(re int i=1; i<n; i++)
		{ down.insert(1,1,tot,val[i].t = lower_bound(tmp+1,tmp+1+tot,val[i].t)-tmp,1); }
		//for(re int i=1; i<=n; i++) { printf("%d %d %d\n",val[i].w,val[i].t,tmp[val[i].t]); }
		for(re int x=1,y=0; x<=n; x+=2,y++)
		{
			while(p)
			{
				//debug("fuck");
				up.sum = down.sum = 0,
				up.cnt = down.cnt = y;
				up.query(1,1,tot),down.query(1,1,tot);
				//if(up.cnt||down.cnt) { break ; }
				//printf("x=%d p=%d\n",x,p);
				//printf("cnt1=%d cnt2=%d\n",up.cnt,down.cnt);
				//printf("sum1=%lld sum2=%lld\n",up.sum,down.sum);
				if(!up.cnt&&!down.cnt&&up.sum+down.sum+1ll*tmp[val[p].t]<=t)
				{ ans[x] = val[p].w; break ; }
				p--;
				up.insert(1,1,tot,val[p+1].t,1),
				down.insert(1,1,tot,val[p].t,-1);
			}
			if(!p) { break ; }
		}
		for(re int i=1,x; i<=q; i++)
		{ cin >> x; printf("%d\n",ans[x]?ans[x]:-1); }
		return 0;
	};
}
signed main()
{ return OMA::main(); }

T2

直接跑最短路有80pts。

100pts:

先dfs一边,找出只走0边就能到达的点。

在经过的边数最小的前提下,让字典序最小,可以直接bfs,先将所有距离为0的点都压入队列,每次从队首取出所有距离相同的点,先遍历0边再遍历1边更新答案。

T3

image

T4

不会,咕。

posted @ 2021-10-28 07:10  -OMA-  阅读(89)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end