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 ...
随机推荐
- redis 简单整理——哨兵部署业务图[二十九]
前言 简单介绍一下哨兵的部署业务图,非部署步骤. 正文 看一下部署的拓扑图: 然后这里用docker 来部署一下哨兵模式. 搭建一主二从. version: '3.7' services: maste ...
- kratos http原理
概念 kratos 为了使http协议的逻辑代码和grpc的逻辑代码使用同一份,选择了基于protobuf的IDL文件使用proto插件生成辅助代码的方式. protoc http插件的地址为:htt ...
- 力扣598(java)-范围求和Ⅱ(简单)
题目: 给你一个 m x n 的矩阵 M ,初始化时所有的 0 和一个操作数组 op ,其中 ops[i] = [ai, bi] 意味着当所有的 0 <= x < ai 和 0 <= ...
- 链路分析 K.O “五大经典问题”
简介:链路分析是基于已存储的全量链路明细数据,自由组合筛选条件与聚合维度进行实时分析,可以满足不同场景的自定义诊断需求. 作者:涯海 链路追踪的 "第三种玩法" 提起链路追踪,大 ...
- Yurt-Tunnel 详解|如何解决 K8s 在云边协同下的运维监控挑战
简介: 伴随着 5G.IoT 等技术的快速发展,边缘计算被越来越广泛地应用于电信.媒体.运输.物流.农业.零售等行业和场景中,成为解决这些领域数据传输效率的关键方式.与此同时,边缘计算形态.规模.复杂 ...
- [GPT] gradio-chatbot 原理及代码解析
GradioChatBot 是一个基于 Gradio 的聊天机器人,它可以与不同的 URL 进行对话.其原理是通过将用户输入的文本发送到指定的 URL,然后接收并解析 URL 返回的响应,然后将响 ...
- WPF 一千个矩形做动画测试性能
在很多性能测试开始之前,都需要测试一下自己的期望优化的设备的性能上限是多少.我每次都是重新写一个测试应用,因为每次需要优化的方向都不相同.本文将记录一个我写的一个简单的测试应用,这里面包含了一千个半透 ...
- 记 Win8.1 某应用渲染抛出 OutOfMemoryException 异常及修复方法
本文记录某个应用在某台 Windows 8.1 x86 系统上,运行时抛出 OutOfMemoryException 异常,启动失败.应用程序能启动,但是在第一次碰到渲染时,就发现渲染初始化失败,从而 ...
- 2019-9-30-dotnet-枚举当前设备wifi热点
title author date CreateTime categories dotnet 枚举当前设备wifi热点 lindexi 2019-09-30 14:42:18 +0800 2019-9 ...
- SpringBoot序列化、反序列化空字符串为null的三种方式
一.需求:接收前端传入的""空字符串参数,有时候我们需要把它转为null SpringBoot项目 方式:①Jackson(推荐).②切面+反射.③注解+切面+反射 后两种方式,未 ...