(联考)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
T4
不会,咕。