HNOI 2017 泛做
可以催更,但是不保证更
但是 \(2015\) 和 \(2016\) 年的题都好水啊,我都能随便切,所以不打算更新了。
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
#include
#include
#include
#include
#include
2017 单旋
题目描述
点此看题
解法
重要的观察:本题的旋转到根操作只涉及最大值和最小值,最值的旋转肯定存在某种性质。
反复手玩发现,最小值的旋转就是把它的右子树接到父亲上,然后直接把整棵树当成它的右儿子,所以父子关系和深度的变化是很少的,最大值的旋转方式类似。
所以我们拿个 \(\tt set\) 找前驱后继,用于插入;拿个单点查询、区间修改的线段树维护深度。
Bonus
如果这题旋转任意值可以做吗,我的研究进度到了一个不知道对不对的结论:如果确定所有所有点的深度那么可以还原整棵树。
#include
#include
#include
#include
using namespace std;
const int M = 100005;
#define sit set::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 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("");
}
}