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 ...
随机推荐
- vscode 编辑python 如何格式化
正文 今天同事说我的代码的格式不对,其实就是几个空格忘了空4格了,但是代码可运行. 那么如何帮我们检测呢? pip install yapf 然后打开setting: 输入:ython.formatt ...
- jenkins 持续集成和交付——一个构件小栗子前置(三)
前言 下面介绍构建一个小栗子. 在此之前有个小前提,就是已经安装好了git 服务器,用的是gogs,详细请看外篇. 正文 插件安装 首先你要安装一个git插件. 装完git插件后,我们还得安装一些gi ...
- Tkinter常用功能示例(一)
技术背景 Tkinter是一个Python自带的GUI框架,虽然现在主流的还是用pyqt的多一些,但是Tkinter在环境配置上可以节省很多工作,可以用来做一些小项目.如果是大型项目,用pyqt或者Q ...
- 华为云CodeArts IDE For Python 快速使用指南
本文分享自华为云社区<华为云CodeArts IDE For Python 快速使用指南>,作者:为云PaaS服务小智. CodeArts IDE 带有 Python 扩展,为 Pytho ...
- uniapp中实现简易计算器
uniapp中实现简易计算器主要问题:在计算器的实现过程中会遇到小数点计算精度:此计算器是依赖了uni-popup的弹出层插件,可在uniapp官方组件中查找扩展插件popup弹窗层下载,也可直接点击 ...
- Ubuntu下部署gitlab
1.安装gitlab服务 1.安装依赖 在ubuntu下使用快捷键ctrl+alt+T打开命令行窗口,然后运行下面命令 sudo apt update sudo apt-get upgrade sud ...
- Solon Java 应用开发框架 v2.7.5 发布
Java Solon 是什么框架? Java "新的"应用开发框架.从零开始构建(非 java-ee 架构),有灵活的接口规范与开放生态. 追求: 更快.更小.更简单 提倡: 克制 ...
- 最佳实践:使用阿里云CDN加速OSS访问
简介: 用户直接访问OSS资源,访问速度会受到OSS的下行带宽以及Bucket地域的限制.如果通过CDN来访问OSS资源,带宽上限更高,并且可以将OSS的资源缓存至就近的CDN节点,通过CDN节点进行 ...
- SRE技术保障平台-盯屏中心TAC: 混合云一站式告警运维平台
简介: SRE技术保障平台-盯屏中心TAC: 混合云一站式告警运维平台 1.目标定位 1.1背景 告警管控平台种类繁多 告警出现后未及时发现处理最终导致故障产生 专有云监控能力拉起依赖版本升级,操作复 ...
- Hologres如何支持亿级用户UV计算
简介: 本文将介绍阿里云Hologres如何基于RoaringBitmap进行UV等高复杂度计算的方案,实现亿级用户万级标签亚秒级分析,帮助用户从Kylin平滑迁移到Hologres,实现更实时.开发 ...