NOIP模拟59
T1 柱状图
解题思路
二分答案+线段树check
显然对于最后的限制,我们希望向上移的和向下移的柱子数尽量接近。
因此枚举每一个柱子当做最高的一个的时刻,二分找到一个当前最优解更新答案。
开两棵线段树分别维护当前柱子左右两侧的柱子的 \(h_i+i\) 和 \(h_i-i\) 每次查询个数用个数以及权值和判断更新即可。
需要卡常,但是我懒直接卡了一下时就走了。。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
#define ls tre[x].l
#define rs tre[x].r
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=1e5+10,INF=1e9;
int n,ans=1e18,maxn,s[N];
bool check(){return (1.0*clock())/(1.0*CLOCKS_PER_SEC)<=1.49;}
struct Segment_Tree
{
int root,all;
Segment_Tree(){all=0;root=0;}
struct Node
{
int l,r,siz,dat;
}tre[N*30];
void push_up(int x){tre[x].siz=tre[ls].siz+tre[rs].siz;tre[x].dat=tre[ls].dat+tre[rs].dat;}
void insert(int &x,int l,int r,int pos,int val)
{
if(!x) x=++all;
if(l==r) return tre[x].siz+=val,tre[x].dat=tre[x].siz*l,void();
int mid=floor((1.0*l+1.0*r)/2.0);
if(pos<=mid) insert(ls,l,mid,pos,val);
else insert(rs,mid+1,r,pos,val);
push_up(x);
}
int query_siz(int x,int l,int r,int L,int R)
{
if(!x) return 0;
if(L<=l&&r<=R) return tre[x].siz;
int mid=floor((1.0*l+1.0*r)/2.0),sum=0;
if(L<=mid) sum+=query_siz(ls,l,mid,L,R);
if(R>mid) sum+=query_siz(rs,mid+1,r,L,R);
return sum;
}
int query_sum(int x,int l,int r,int L,int R)
{
if(!x) return 0;
if(L<=l&&r<=R) return tre[x].dat;
int mid=floor((1.0*l+1.0*r)/2.0),sum=0;
if(L<=mid) sum+=query_sum(ls,l,mid,L,R);
if(R>mid) sum+=query_sum(rs,mid+1,r,L,R);
return sum;
}
}L,R;
int solve(int x,int val)
{
int tmp1=L.query_siz(L.root,-N,INF+N,-N,val-x-1)*(val-x)-L.query_sum(L.root,-N,INF+N,-N,val-x-1);
int tmp2=L.query_sum(L.root,-N,INF+N,val-x+1,INF+N)-L.query_siz(L.root,-N,INF+N,val-x+1,INF+N)*(val-x);
int tmp3=R.query_siz(R.root,-N,INF+N,-N,val+x-1)*(val+x)-R.query_sum(R.root,-N,INF+N,-N,val+x-1);
int tmp4=R.query_sum(R.root,-N,INF+N,val+x+1,INF+N)-R.query_siz(R.root,-N,INF+N,val+x+1,INF+N)*(val+x);
if(tmp1+tmp2+tmp3+tmp4<0) return INF*INF;
return tmp1+tmp2+tmp3+tmp4;
}
signed main()
{
freopen("c.in","r",stdin); freopen("c.out","w",stdout);
n=read();
for(int i=1;i<=n;i++) s[i]=read(),maxn=max(maxn,s[i]+i);
for(int i=1;i<=n;i++) R.insert(R.root,-N,INF+N,s[i]+i,1);
for(int i=1;i<=n;i++)
{
if(!check()) break;
int l=max(i,n-i+1),r=maxn;
while(l+1<r)
{
int mid=(l+r)>>1;
int temp=L.query_siz(L.root,-N,INF+N,-N,mid-i-1)+R.query_siz(R.root,-N,INF+N,-N,mid+i-1);
if(temp<=n-temp-1) l=mid;
else r=mid;
}
ans=min(ans,min(solve(i,l),solve(i,r)));
R.insert(R.root,-N,INF+N,s[i]+i,-1);
L.insert(L.root,-N,INF+N,s[i]-i,1);
}
printf("%lld",ans);
return 0;
}
T2 应急棍
大坑未补
需要高精小数,并且比较难打,咕了。
T3 擒敌拳
解题思路
李超线段树维护单调栈。
大概是个板子了吧,尽管我刚刚学会,单调栈预处理出某个高度可行的区间,在这个区间中插入一条直线其实就是插入一条线段。
然后对于标记下放的时候记录当前区间比较占优势的线段的斜率截距,也就是区间中点取值较优的一个。
优于我们既想要更新答案又想要同时保留两个值,因此就有一个 swap 的操作。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
#define ls x<<1
#define rs x<<1|1
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=2e5+10,INF=1e18;
int n,top,sta[N],s[N],l[N],r[N];
struct Segment_Tree{int k,b;}tre[N<<2];
int g(int x,int k,int b){return x*k+b;}
void insert(int x,int l,int r,int k,int b)
{
if(g(l,tre[x].k,tre[x].b)<=g(l,k,b)&&g(r,tre[x].k,tre[x].b)<=g(r,k,b)) return tre[x].k=k,tre[x].b=b,void();
if(l==r) return;
int mid=(l+r)>>1;
if(g(mid,tre[x].k,tre[x].b)<=g(mid,k,b)) swap(tre[x].k,k),swap(tre[x].b,b);
if(g(l,tre[x].k,tre[x].b)<=g(l,k,b)) insert(ls,l,mid,k,b);
if(g(r,tre[x].k,tre[x].b)<=g(r,k,b)) insert(rs,mid+1,r,k,b);
}
void update(int x,int l,int r,int L,int R,int k,int b)
{
if(L<=l&&r<=R) return insert(x,l,r,k,b),void();
int mid=(l+r)>>1;
if(L<=mid) update(ls,l,mid,L,R,k,b);
if(R>mid) update(rs,mid+1,r,L,R,k,b);
}
int query(int x,int l,int r,int pos)
{
if(l==r) return g(l,tre[x].k,tre[x].b);
int mid=(l+r)>>1,dat=g(pos,tre[x].k,tre[x].b);
if(pos<=mid) dat=max(dat,query(ls,l,mid,pos));
else dat=max(dat,query(rs,mid+1,r,pos));
return dat;
}
signed main()
{
freopen("b.in","r",stdin); freopen("b.out","w",stdout);
n=read(); for(int i=1;i<=n;i++) s[i]=read(),l[i]=r[i]=i;
for(int i=1;i<=n;i++)
{
while(top&&s[i]<=s[sta[top]]) l[i]=l[sta[top--]];
sta[++top]=i;
}
top=0;
for(int i=n;i>=1;i--)
{
while(top&&s[i]<=s[sta[top]]) r[i]=r[sta[top--]];
sta[++top]=i;
}
for(int i=1,maxn=0;i<=n;i++)
{
update(1,1,n,l[i],r[i],s[i],-(l[i]-1)*s[i]);
printf("%lld ",maxn=max(maxn,query(1,1,n,i)));
}
return 0;
}
T4 连接(边数)
解题思路
显然是一个基环树森林, 1 节点需要向每一个树的叶子节点连边,然后每隔 k 连接一个点。
然后就是处理环的情况,可以倍增跳看全部覆盖之后是否是最优解,也可以枚举每个点在所覆盖的 k 中的位置,对于本题而言复杂度都允许。。。
code
#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
const int N=5e5+10,INF=1e18;
int n,m,cnt,all,top,ans,tim,sta[N],du[N];
int f[N][25],s[N<<1],near[N<<1],pre[N<<1];
int tot=1,head[N],nxt[N<<1],ver[N<<1];
bool b[N],vis[N],jud[N],suc[N];
set<int> se;
void add_edge(int x,int y)
{
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
du[y]++;
}
void dfs(int x)
{
if(!b[x])
{
b[x]=true; if(vis[x]) return b[x]=false,void();
for(int i=head[x];i;i=nxt[i]) dfs(ver[i]);
b[x]=false; vis[x]=true;
return ;
}
int temp=x; jud[x]=true;
do
{
temp=ver[head[temp]];
jud[temp]=true;
}while(temp!=x);
}
void SPJ()
{
suc[1]=true;
int to=ver[head[1]];
du[to]--; suc[to]=true;
if(se.find(1)!=se.end()) se.erase(1);
if(se.find(to)!=se.end()) se.erase(to);
}
void topo_sort()
{
queue<int> q;
if(!suc[1]) SPJ(),q.push(ver[head[1]]);
for(auto it=se.begin();it!=se.end();it++)
if(!du[(*it)]&&!jud[(*it)]&&!suc[(*it)])
ans++,suc[(*it)]=true,q.push((*it));
while(!q.empty())
{
int x=q.front(),temp=m-1,fro=0; suc[x]=true; q.pop();
while(temp--)
{
fro=x; x=ver[head[x]]; if(!du[fro]) du[x]--; suc[x]=true;
if(!du[fro]&&se.find(fro)!=se.end()) se.erase(fro);
}
fro=x; x=ver[head[x]]; if(!du[fro]) du[x]--;
if(!du[fro]&&se.find(fro)!=se.end()) se.erase(fro);
if(!du[x]&&!jud[x]&&!suc[x]){q.push(x);ans++;suc[x]=true;continue;}
while(!du[x]&&suc[x])
{
if(se.find(x)!=se.end()) se.erase(x);
x=ver[head[x]]; du[x]--;
}
}
}
int solve(int x)
{
int len=0,minn=INF,temp=x;
do
{
temp=ver[head[temp]];
s[++len]=temp;
}while(temp!=x);
for(int i=len+1;i<=2*len;i++) s[i]=s[i-len];
near[2*len+1]=2*len+1; pre[2*len+1]=INF;
for(int i=2*len;i>=1;i--)
if(!suc[s[i]]) near[i]=i;
else near[i]=near[i+1];
for(int i=1;i<=len*2;i++) pre[i]=pre[i-1]+(suc[s[i]]^1);
for(int i=2*len+1;i>=1;i--)
{
f[i][0]=near[min(i+m,len*2+1)];
for(int j=1;j<=20;j++)
f[i][j]=f[f[i][j-1]][j-1];
}
for(int i=1;i<=len;i++)
{
int t=0,pos=i;
for(int j=20;j>=0;j--)
if(f[pos][j]<=i+len) pos=f[pos][j],t+=1ll<<j;
if(pos<=i+len-1&&pre[pos-1]!=pre[i+len]) t++;
minn=min(minn,t);
}
for(int i=1;i<=len;i++) suc[s[i]]=true;
return minn;
}
signed main()
{
freopen("d.in","r",stdin); freopen("d.out","w",stdout);
n=read(); m=read();
for(int i=1,x,y;i<=n;i++) x=read(),y=read(),add_edge(x,y);
for(int i=1;i<=n;i++) if(!vis[i]) dfs(i);
for(int i=1;i<=n;i++) if(!jud[i]) se.insert(i);
while(se.size()) topo_sort();
for(int i=1;i<=n;i++) if(jud[i]&&!suc[i]) ans+=solve(i);
printf("%lld",ans);
return 0;
}
NOIP模拟59的更多相关文章
- Noip模拟59 2021.9.22
新机房首模拟变倒数 T1 柱状图 关于每一个点可以做出两条斜率分别为$1,-1$的直线, 然后题意转化为移动最少的步数使得所有点都在某一个点的两条直线上 二分出直线的高度,判断条件是尽量让这条直线上部 ...
- 2021.9.22考试总结[NOIP模拟59]
T1 柱状图 关于每个点可以作出两条斜率绝对值为\(1\)的直线. 将绝对值拆开,对在\(i\)左边的点\(j\),\(h_i-i=h_j-j\),右边则是把减号换成加号. 把每个点位置为横坐标,高度 ...
- 队爷的讲学计划 CH Round #59 - OrzCC杯NOIP模拟赛day1
题目:http://ch.ezoj.tk/contest/CH%20Round%20%2359%20-%20OrzCC杯NOIP模拟赛day1/队爷的讲学计划 题解:刚开始理解题意理解了好半天,然后发 ...
- 队爷的Au Plan CH Round #59 - OrzCC杯NOIP模拟赛day1
题目:http://ch.ezoj.tk/contest/CH%20Round%20%2359%20-%20OrzCC杯NOIP模拟赛day1/队爷的Au%20Plan 题解:看了题之后觉得肯定是DP ...
- 队爷的新书 CH Round #59 - OrzCC杯NOIP模拟赛day1
题目:http://ch.ezoj.tk/contest/CH%20Round%20%2359%20-%20OrzCC杯NOIP模拟赛day1/队爷的新书 题解:看到这题就想到了 poetize 的封 ...
- NOIP模拟 1
NOIP模拟1,到现在时间已经比较长了.. 那天是6.14,今天7.18了 //然鹅我看着最前边缺失的模拟1,还是终于忍不住把它补上,为了保持顺序2345重新发布了一遍.. # 用 户 名 ...
- NOIP模拟17.9.22
NOIP模拟17.9.22 前进![问题描述]数轴的原点上有一只青蛙.青蛙要跳到数轴上≥
- NOIP 模拟4 T2
本题属于二和一问题 子问题相互对称 考虑对于问题一:知a求b 那么根据b数组定义式 显然能发现问题在于如何求dis(最短路) 有很多算法可供选择 dijsktra,floyed,bfs/dfs,spf ...
- noip模拟32[好数学啊]
noip模拟32 solutions 真是无语子,又没上100,无奈死了 虽然我每次都觉得题很难,但是还是有好多上100的 战神都200多了,好生气啊啊啊 从题开始变难之后,我的时间分配越来越不均匀, ...
- 2021.9.17考试总结[NOIP模拟55]
有的考试表面上自称NOIP模拟,背地里却是绍兴一中NOI模拟 吓得我直接文件打错 T1 Skip 设状态$f_i$为最后一次选$i$在$i$时的最优解.有$f_i=max_{j<i}[f_j+a ...
随机推荐
- Ubuntu下部署gitlab
1.安装gitlab服务 1.安装依赖 在ubuntu下使用快捷键ctrl+alt+T打开命令行窗口,然后运行下面命令 sudo apt update sudo apt-get upgrade sud ...
- DM 传统行业SQL优化案例
来OB这么久还没有接触啥金融的SQL,只能发点其他行业的数据库SQL优化案例. 今天拿到手的这个案例SQL 传统行业的,很奇葩的SQL,表设计三范式都没弄好. 什么医疗,交通,能源这些传统行业的业务设 ...
- HarmonyOS NEXT应用开发之深色跑马灯案例
介绍 本示例介绍了文本宽度过宽时,如何实现文本首尾相接循环滚动并显示在可视区,以及每循环滚动一次之后会停滞一段时间后再滚动. 效果图预览 使用说明: 1.进入页面,检票口文本处,实现文本首尾相接循环滚 ...
- Delta Lake基础介绍(商业版)
简介:介绍 Lakehouse 搜索引擎的设计思想,探讨其如何使用缓存,辅助数据结构,存储格式,动态文件剪枝,以及 vectorized execution 达到优越的处理性能. 作者:李洁杏,Da ...
- 基于 Flutter 的 Web 渲染引擎「北海」正式开源!
简介: 阿里巴巴历时 3 年自研开发的 Web 渲染引擎北海(英文名:Kraken)正式开源,致力打造易扩展,跨平台,高性能的渲染引擎,并已在优酷.大麦.天猫等业务场景中使用. 作者 | 染陌来源 | ...
- 编译优化 | LLVM代码生成技术详解及在数据库中的应用
简介: 作者:长别 1. 前言 随着IT基础设施的发展,现代的数据处理系统需要处理更多的数据.支持更为复杂的算法.数据量的增长和算法的复杂化,为数据分析系统带来了严峻的性能挑战.近年来,我们可以在数据 ...
- 【ESSD技术解读-02】企业级利器,阿里云 NVMe 盘和共享存储
简介: 当前 NVMe 云盘结合了业界最先进的软硬件技术,在云存储市场,首创性同时实现了 NVMe 协议 + 共享访问 + IO Fencing 技术.它在 ESSD 之上获得了高可靠.高可用.高性能 ...
- ARM 反汇编速成
1.跳转指令 B 无条件跳转 BL 带链接的无条件跳转 BX 带状态切换的无条件跳转 BLX 带链接和状态切换的无条件跳转 B loc_地址 BNE, BEQ 2.存储器与寄存器交互数据指令 ...
- [FE] WebStorm, ESLint: Trailing spaces not allowed
在 WebStorm 中搜索文件 .eslintrc.js 在里面的 rules 项中追加规则: 'no-trailing-spaces' : ['off', { 'skipBlankLines': ...
- [Contract] Solidity 多种访问控制 (Access Control) 实现方式
在 solidity 中控制访问,一般是通过 modifier 修饰符方法来直接做. 那么对于稍复杂的多种访问控制,通常需要一个统一操作的模块化类库. 现在已经有了这样的类库存在,我们通过一个实现功能 ...