NOIP 模拟 17

A 镜的绮想

直接做,不过如果 n=2e5 咋做?

B 万物有灵

倒着选一定最优,然后每 K 层是一个周期,为了避免分讨,使 K2K,写完贡献的式子是等比数列,但是这题卡逆元,所以用矩阵加速或者倍增求和即可。

C 白石溪

n2 DP 容易想到,但是无论如何都需要石子数量的状态,整个是 2D/0D,没有可以优化的地方。
发现这个东西推一下贡献的式子很有前途,先考虑一个红石子的贡献,f(i)=ai+d(iprei),其中 prei 表示 [1,i] 中与 i 同色的数量,对于蓝石子同理,简单化简一下石子,所有红石子的贡献就是 ai+d(iprei)=ai+diprei,发现后面这一项已经跟 i 没关系了,只需要考虑数量即可,所以可以先全涂成蓝色,然后把一些位置改成蓝色,改 i 位置的贡献是 aibi+dicinumred+numbluenum 是实时更改的,与前面没关系,所以直接贪心的按 aibi+dici 排序即可。

D 上山岗

容易得出数量,考虑每一位的答案,肯定是在保证数量的时候尽可能大,但是越大对后面的影响就越大,所以讨论一下这个能不能匹配上后二分答案即可,时间复杂度为 O(n2logn)
发现上面的东西还是很有前途的,不过每次二分后需要 O(n)check 很不好,所以思考怎样快速得出田忌赛马问题的答案。
田忌赛马问题本质上是二分图匹配,求最大匹配数自然可以想到 Hall 定理,根据 Hall 的推广得出:

  • 将山看做 1,人看做 1,将山和人一起排序后最大匹配数为 n 加上最小前缀和。

最小前缀和可以使用线段树维护,这时二分能做到 O(nlog2n),直接在线段树上二分就能做到 O(nlogn)

#include<bits/stdc++.h>
#define ls p<<1
#define rs p<<1|1
#define fi first
#define se second
#define pii std::pair<int,int>
#define eb emplace_back
#define pb push_back
typedef long long ll;
typedef unsigned long long ull;
std::mt19937 myrand(std::chrono::high_resolution_clock::now().time_since_epoch().count());
inline int R(int n){return myrand()%n+1;}
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=1e6+10,inf=1e9;
inline void Min(int &x,int y){if(x>y)x=y;}
inline void Max(int &x,int y){if(x<y)x=y;}
int n,LEN,v[N],h[N],a[N],pos[2][N];
pii s[N];
struct NODE{int tag,min,num;}t[N<<2];
inline void pushdown(int p){int &x=t[p].tag;t[ls].tag+=x;t[ls].min+=x;t[rs].tag+=x;t[rs].min+=x;x=0;}
inline void update(int p){t[p].min=std::min(t[ls].min,t[rs].min);t[p].num=t[ls].num+t[rs].num;}
inline void build(int p,int l,int r){
if(l==r){t[p].min=v[l],t[p].num=s[l].se>n;return;}
int mid=l+r>>1;build(ls,l,mid);build(rs,mid+1,r);update(p);
}
inline void change(int p,int l,int r,int L,int R,int x){
if(l>=L&&r<=R){t[p].tag+=x,t[p].min+=x;return ;}int mid=l+r>>1;if(t[p].tag)pushdown(p);
if(L<=mid)change(ls,l,mid,L,R,x);if(R>mid)change(rs,mid+1,r,L,R,x);update(p);
}
inline void _change(int p,int l,int r,int pos){
if(l==r)return t[p].num=0,void();if(t[p].tag)pushdown(p);int mid=l+r>>1;
if(pos<=mid)_change(ls,l,mid,pos);else _change(rs,mid+1,r,pos);update(p);
}
inline int query(int p,int l,int r,int lim){
if(!t[p].num)return -1;if(l==r)return t[p].min>=lim-1?l:-1;
if(t[p].tag)pushdown(p);int mid=l+r>>1;
if(t[ls].min<lim)return query(ls,l,mid,lim);
int res=query(rs,mid+1,r,lim);
return res>0?res:query(ls,l,mid,lim);
}
signed main(){
freopen("uphill.in","r",stdin);freopen("uphill.out","w",stdout);
std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
n=read();for(int i=1;i<=n;++i)h[i]=read()+1,s[i]={h[i],i};LEN=2*n;
for(int i=1;i<=n;++i)a[i]=read(),s[i+n]={a[i],i+n};
std::sort(s+1,s+LEN+1);
for(int i=1;i<=LEN;++i)
if(s[i].se<=n)v[i]=v[i-1]+1,pos[0][s[i].se]=i;
else v[i]=v[i-1]-1,pos[1][s[i].se-n]=i;
build(1,1,LEN);
for(int i=1,tot=n+t[1].min;i<=n;++i){
change(1,1,LEN,pos[0][i],LEN,-1);
int p=query(1,1,LEN,tot-1-(n-i));
if(p<pos[0][i])p=query(1,1,LEN,tot-(n-i));else tot--;
change(1,1,LEN,p,LEN,1);_change(1,1,LEN,p);std::cout<<s[p].fi<<' ';
}std::cout<<'\n';
}

总结

打的不行,T3 想到推下贡献的式子但是没去做,T4 二分都没想到,菜,T2 的暴力没有赋初值,都什么时候了还在这种地方挂分。

posted @   Ishar-zdl  阅读(47)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示