HNOI 2014-2017 泛做

可以催更,但是不保证更

2017 大佬

题目描述

点此看题

解法

关于虽然评分是黑但是我还是感觉好水并且还是要写题解这件事

观察发现存活和攻击是两件独立的事,所以对于最优方案我们只需要求出在保证存活的情况下能拿去攻击的最大天数,这可以用一个简单的背包解决。

在判断能否击败大佬的时候,我们可以考虑把两个大招拆分,对于一次大招我们可以用一个三元组来表示:(day,F,L),考虑到最多只有 \(100\) 天,所以 \(F\) 的取值应该不会很多。那么我们可以暴力 \(\tt bfs\) 出所有的三元组,但是 \(\tt hash\) 的时候只用保留 (F,L),天数取最小的一定最优。

然后考虑找出两个大招来拼凑出最优解,考虑不会让大佬血量低于 \(0\) 并且最后能击败的条件是:

\[F_1+F_2\leq c_i\ \and \ c_i-F_1-F_2\leq MaxDay-day_1-day_2 \]

那么第一个条件可以双指针,维护最小的 \(day_2-F_2\) 来保证第二个条件。还有一些情况是不使用大招或者只使用一次大招,由于过于简单在此不必赘述。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
#include <map>
using namespace std;
const int M = 1005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,mc,up,Day,a[M],b[M],c[M],dp[M][M];
struct node{int d,f,l;};map<pair<int,int>,int> mp;
struct shit
{
	int x,y;
	bool operator < (const shit &b) const
	{return y<b.y;}
}s[10000005];
void bfs()
{
	queue<node> q;q.push(node{1,1,0});
	while(!q.empty())
	{
		node u=q.front();q.pop();
		if(u.d==Day) continue;
		q.push(node{u.d+1,u.f,u.l+1});
		long long t=1ll*u.f*u.l;
		if(u.l>1 && t<=up && !mp.count(make_pair(t,u.l)))
		{
			mp[make_pair(t,u.l)]=1;s[++k]=shit{u.d+1,t};
			q.push(node{u.d+1,t,u.l});
		}
	}
}
signed main()
{
	n=read();m=read();mc=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<=n;i++) b[i]=read();
	for(int i=1;i<=m;i++) up=max(up,c[i]=read());
	for(int i=1;i<=n;i++)
		for(int j=a[i];j<=mc;j++)
		{
			int id=min(j-a[i]+b[i],mc);
			dp[i][j-a[i]]=max(dp[i][j-a[i]],dp[i-1][j]+1);
			dp[i][id]=max(dp[i][id],dp[i-1][j]);
		}
	for(int i=1;i<=n;i++)
		for(int j=0;j<=mc;j++)
			Day=max(Day,dp[i][j]);
	bfs();sort(s+1,s+1+k);
	for(int i=1;i<=m;i++)
	{
		if(c[i]<=Day) {puts("1");continue;}
		int fl=0,mn=0;
		for(int j=k,p=1;j>=1;j--)
		{
			while(p<=k && s[j].y+s[p].y<=c[i])
				mn=min(mn,s[p].x-s[p].y),p++;
			if(s[j].y<=c[i] && mn+c[i]-s[j].y<=Day-s[j].x)
				{fl=1;break;}
		}
		printf("%d\n",fl);
	}
}

2017 单旋

题目描述

点此看题

解法

重要的观察:本题的旋转到根操作只涉及最大值和最小值,最值的旋转肯定存在某种性质。

反复手玩发现,最小值的旋转就是把它的右子树接到父亲上,然后直接把整棵树当成它的右儿子,所以父子关系和深度的变化是很少的,最大值的旋转方式类似。

所以我们拿个 \(\tt set\) 找前驱后继,用于插入;拿个单点查询、区间修改的线段树维护深度。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <set>
using namespace std;
const int M = 100005;
#define sit set<int>::iterator
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
void write(int x)
{
	if(x>=10) write(x/10);
	putchar(x%10+'0');
}
int n,m,a[M],b[M],c[M];
//namespace segment-tree
int ad[M<<2],tr[M<<2];
void down(int i)
{
	if(!ad[i]) return ;
	tr[i<<1]+=ad[i];ad[i<<1]+=ad[i];
	tr[i<<1|1]+=ad[i];ad[i<<1|1]+=ad[i];
	ad[i]=0;
}
void add(int i,int l,int r,int L,int R,int c)
{
	if(l>R || L>r) return ;
	if(L<=l && r<=R) {tr[i]+=c;ad[i]+=c;return ;}
	int mid=(l+r)>>1;down(i);
	add(i<<1,l,mid,L,R,c);
	add(i<<1|1,mid+1,r,L,R,c);
	tr[i]=tr[i<<1]+tr[i<<1|1]; 
}
void ins(int i,int l,int r,int id,int c)
{
	if(l==r) {tr[i]=c;return ;}
	int mid=(l+r)>>1;down(i);
	if(mid>=id) ins(i<<1,l,mid,id,c);
	else ins(i<<1|1,mid+1,r,id,c);
	tr[i]=tr[i<<1]+tr[i<<1|1];
}
int ask(int i,int l,int r,int id)
{
	if(l==r) return tr[i];
	int mid=(l+r)>>1;down(i);
	if(mid>=id) return ask(i<<1,l,mid,id);
	return ask(i<<1|1,mid+1,r,id);
}
//using set to maintain spaly
set<int> s;int rt,fa[M],ch[M][2];
int fuck(int x)
{
	if(!rt) {rt=x;s.insert(x);ins(1,1,n,x,1);return 1;}
	sit it=s.lower_bound(x);int ans=0;
	if(it!=s.end() && !ch[*it][0])
	{
		ch[*it][0]=x;fa[x]=*it;
		ins(1,1,n,x,ans=ask(1,1,n,*it)+1);
		s.insert(x);return ans;
	}
	it--;ch[*it][1]=x;fa[x]=*it;
	ins(1,1,n,x,ans=ask(1,1,n,*it)+1);
	s.insert(x);return ans;
}
int spmin()
{
	int v=*s.begin();
	if(v==rt) return 1;
	int d=ask(1,1,n,v);
	ins(1,1,n,v,1);add(1,1,n,fa[v],m,1);
	ch[fa[v]][0]=ch[v][1];fa[ch[v][1]]=fa[v];
	fa[v]=0;ch[v][1]=rt;fa[rt]=v;rt=v;return d;
}
int spmax()
{
	int v=*s.rbegin();
	if(v==rt) return 1;
	int d=ask(1,1,n,v);
	ins(1,1,n,v,1);add(1,1,n,1,fa[v],1);
	ch[fa[v]][1]=ch[v][0];fa[ch[v][0]]=fa[v];
	fa[v]=0;ch[v][0]=rt;fa[rt]=v;rt=v;return d;
}
int delmin()
{
	int d=spmin();s.erase(rt);
	int tmp=rt;rt=ch[tmp][1];ch[tmp][1]=0;
	add(1,1,n,1,n,-1);return d;
}
int delmax()
{
	int d=spmax();s.erase(rt);
	int tmp=rt;rt=ch[tmp][0];ch[tmp][0]=0;
	add(1,1,n,1,n,-1);return d;
}
signed main()
{
	m=read();
	for(int i=1;i<=m;i++)
	{
		a[i]=read();
		if(a[i]==1) c[++n]=b[i]=read();
	}
	sort(c+1,c+1+n);n=unique(c+1,c+1+n)-c-1;
	for(int i=1;i<=m;i++) if(a[i]==1)
		b[i]=lower_bound(c+1,c+1+n,b[i])-c;
	for(int i=1;i<=m;i++)
	{
		if(a[i]==1) write(fuck(b[i]));
		if(a[i]==2) write(spmin());
		if(a[i]==3) write(spmax());
		if(a[i]==4) write(delmin());
		if(a[i]==5) write(delmax());
		puts("");
	}
}
上一篇:jstl分割字符串


下一篇:中国半胱胺盐酸盐行业深度调研与投资战略报告(2022版)