题解 YMOI 2019.6.8

前言

第二回考试,承让拿了第一次rank1,(●ˇ∀ˇ●)

题解

这次考试总体发挥比较好,每一道题都尽可能得取得了所能及的所有分。虽然多少还是有失误,不过在所难免。保持这种状态,继续努力。争取明年让某辣鸡跪着叫大佬┗|`O′|┛

T1 奆炮的重生

题干易懂,思路好想,良心题!

这个嘛,看一眼就能和某《石子合并》能联系上。如果说有什么难点,可能也就是存在负数,需要同时处理最大值和最小值吧

话是这么说的,实际得分\(60分\)真是狠狠打脸了..切记!想好范围再开数组想好范围再开数组想好范围再开数组

本来最有信心的一道题取到了三题分数的min

code:

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll; const int MAX = 102;
const ll INF = 0x7fffffffffffffff; int T, n;
ll ans;
ll f[MAX][MAX][2]; void work(); int main() {
freopen("pao.in", "r", stdin);
freopen("pao.out", "w", stdout); scanf("%d", &T);
while (T--) work(); return 0;
} void work() {
scanf("%d", &n);
for (int i = 1; i <= (n << 1); ++i)
for (int j = i; j <= (n << 1); ++j) f[i][j][0] = INF, f[i][j][1] = -INF; for (int i = 1; i <= n; ++i) scanf("%lld", &f[i][i][0]);
for (int i = 1; i <= n; ++i) f[i + n][i + n][0] = f[i][i][0];
for (int i = 1; i <= (n << 1); ++i) f[i][i][1] = f[i][i][0]; for (int i = 1; i <= (n << 1) - 1; ++i)
f[i][i + 1][0] = f[i][i][0] + f[i + 1][i + 1][0], f[i][i + 1][1] = f[i][i + 1][0]; for (int i = (n << 1) - 2; i >= 1; --i) {
for (int j = i + 2; j <= (n << 1); ++j) {
for (int k = i; k <= j - 1; ++k) { // i~k k+1~j
f[i][j][1] = max(f[i][j][1], f[i][k][1] + f[k + 1][j][1]);
f[i][j][0] = min(f[i][j][0], f[i][k][0] + f[k + 1][j][0]);
}
for (int m = i; m <= j - 2; ++m) { // i~m m+1~n n+1~j
for (int n = m + 1; n <= j - 1; ++n) {
f[i][j][1] = max(f[i][j][1], f[i][m][1] * f[n + 1][j][1] - f[m + 1][n][0]);
f[i][j][1] = max(f[i][j][1], f[i][m][0] * f[n + 1][j][1] - f[m + 1][n][0]);
f[i][j][1] = max(f[i][j][1], f[i][m][1] * f[n + 1][j][0] - f[m + 1][n][0]);
f[i][j][1] = max(f[i][j][1], f[i][m][0] * f[n + 1][j][0] - f[m + 1][n][0]);
f[i][j][0] = min(f[i][j][0], f[i][m][1] * f[n + 1][j][1] - f[m + 1][n][1]);
f[i][j][0] = min(f[i][j][0], f[i][m][0] * f[n + 1][j][1] - f[m + 1][n][1]);
f[i][j][0] = min(f[i][j][0], f[i][m][1] * f[n + 1][j][0] - f[m + 1][n][1]);
f[i][j][0] = min(f[i][j][0], f[i][m][0] * f[n + 1][j][0] - f[m + 1][n][1]);
}
}
}
} ans = -INF;
for (int i = 1; i <= n; ++i) ans = max(ans, f[i][i + n - 1][1]);
printf("%lld\n", ans);
}

T2 幽香的宴会

题目链接

吐槽!!什么鬼题面!!虽说题干是稍微有一点复杂,但也不至于代入世界观吧ㄟ( ▔, ▔ )ㄏ

当初看到这道题的时候就注意到了非强制在线,觉得有东西可搞,但是见识短浅,在考场的时候就是没能想出来

在考场上打了一个暴力,就是每一次都按照询问跑一遍BFS,然后用大根堆筛选前k个。没想到数据足够良心,竟然给了64分的暴力分

正解的想法用到了利用到了非强制在线。这道题有一点比较烦就是在不同情况下,图的形态是不确定的,这样导致我们每一次做都要推翻重来。假如我们事先把询问的降雨大小按照从高到低排序,就变成了向图里一点一点地加边。边加边变维护有效信息,整个操作只需要对图处理一遍,效率大大提高。

然后看看我们面临着怎么样的询问:

Q: 询问从c点开始,哪一片是互相连通的?

A: 这个嘛,并查集轻松解决

Q: 询问这些点的前k大值

A: 这个就涉及到了并查集的合并。每一个并查集都附赠一个小根堆来存某一篇区域的前k个值,利用这个小根堆去维护,转移即可。注意,小根堆的转移直接暴力倒出倒入即可,不需要其它优化

酱紫,就搞定了正解。

话说考试当天还真下雷阵雨了

code:

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std; const int MAX=2e5+5,INF=0x3f3f3f3f; struct data1{
int u,v; double w; bool operator < (data1 &x) const{
return w>x.w;
}
}edge[MAX]; struct data2{
int c,p,id,ans;
}ask[MAX]; int n,m,t,k,c,p;
int dili[MAX];
int adding;
int fa[MAX],fa_size[MAX],fa_dep[MAX],fa_corn_sum[MAX]; priority_queue <int,vector<int>,greater<int> > fa_corn[MAX]; inline int read();
inline void insert(int,int,int,int);
void dijkstra(int,int); int find(int);
void unionn(int,int);
bool check_in(int,int);
int check_size(int);
int check_corn(int); bool cmpp(data2,data2);
bool cmpi(data2,data2); int main(){
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
#endif n=read(); m=read();
for(int i=1;i<=n;++i) dili[i]=read(); for(int i=1;i<=m;++i){
int u,v; u=read(); v=read();
double p; scanf("%lf",&p);
edge[i].u=u; edge[i].v=v; edge[i].w=p;
} t=read(); k=read(); for(int i=1;i<=t;++i){
int c,p; c=read(); p=read();
ask[i].c=c; ask[i].p=p; ask[i].id=i;
} sort(edge+1,edge+m+1);
sort(ask+1,ask+t+1,cmpp); for(int i=1;i<=n;++i) fa[i]=i,fa_size[i]=1,fa_dep[i]=1,fa_corn[i].push(dili[i]);
for(int i=1;i<=n;++i) fa_corn_sum[i]=dili[i]; for(int k=1;k<=t;++k){
while(adding<m&&edge[adding+1].w>=ask[k].p){
adding++;
unionn(edge[adding].u,edge[adding].v);
}
ask[k].ans=check_corn(ask[k].c);
} sort(ask+1,ask+t+1,cmpi); for(int i=1;i<=t;++i) printf("%d\n",ask[i].ans); return 0;
} inline int read(){
char tmp=getchar(); int sum=0; bool flag=false;
while(tmp<'0'||tmp>'9'){
if(tmp=='-') flag=true;
tmp=getchar();
}
while(tmp>='0'&&tmp<='9'){
sum=(sum<<1)+(sum<<3)+tmp-'0';
tmp=getchar();
}
return flag?-sum:sum;
} int find(int x){
if(x==fa[x]) return x;
else return find(fa[x]);
} void unionn(int a,int b){
int fua=find(a);
int fb=find(b);
if(fua==fb) return;
if(fa_dep[fua]<fa_dep[fb]) swap(fua,fb); if(fa_size[fua]+fa_size[fb]<=k){
while(!fa_corn[fb].empty()){
int tmp=fa_corn[fb].top(); fa_corn[fb].pop();
fa_corn_sum[fua]+=tmp;
fa_corn[fua].push(tmp);
}
fa_size[fua]+=fa_size[fb];
}
else{
while(fa_corn[fua].size()+fa_corn[fb].size()>k){
int tmpa=fa_corn[fua].top();
int tmpb=fa_corn[fb].top();
if(tmpa<tmpb){
fa_corn[fua].pop();
fa_corn_sum[fua]-=tmpa;
}
else{
fa_corn[fb].pop();
fa_corn_sum[fb]-=tmpb;
}
}
fa_corn_sum[fua]+=fa_corn_sum[fb];
while(!fa_corn[fb].empty()){
fa_corn[fua].push(fa_corn[fb].top());
fa_corn[fb].pop();
}
fa_size[fua]=k;
} fa[fb]=fua;
if(fa_dep[fua]==fa_dep[fb]) fa_dep[fua]++; } bool check_in(int a,int b){
int fua=find(a);
int fb=find(b);
return fua==fb;
} int check_size(int a){
int fua=find(a);
return fa_size[fua];
} int check_corn(int a){
int fua=find(a);
return fa_corn_sum[fua];
} bool cmpp(data2 a,data2 b){
return a.p>b.p;
} bool cmpi(data2 a,data2 b){
return a.id<b.id;
}

T3 玉米的丰收

据说是某年NOI题的魔改,sjb大佬又来祸害人了(ノ`Д)ノ

题干真是相当新颖,没想到还有鬼才出题人想到点可以在边上,还是个能解决的问题

首先坦白,这道题我没有完全理解,我是用退火骗分才ac的。但是毕竟是道NOI题嘛,暂时先放一放

对于这道题的这个最优点,思考以下发现会有以下性质:

  1. 最优位置到至少两个点的距离等于最远距离【若只有一个点,则将最优位置向靠近这个点

    的方向移动会使最远距离变小】。

  2. 到最优位置距离等于最远距离的点中至少存在一对点在最优位置的两侧(最优位置在以这

    对点为两端的一条链的中点处)【若所有点都在同侧,则将最优位置向这一侧移动将使最

    远距离变小】。

因此最远距离等于图上某条链的中点上。由于是整数的一般,可能出现 \(\frac{1}{2}\) 的倍数。因此事先将所有点翻倍方便处理

然后还需要一点剪枝:

如果当前枚举的边 \(x \Leftrightarrow y\) 的距离 \(d[x][y] \geq ans\times 2\) 那么答案不可能更优,直接舍掉

如果当前枚举的边 \(x \Leftrightarrow y\) 的距离 \(d[x][y]\) (直线距离) \(> d[x][y]\) (最短距离),答案也不可能更优,画图即可理解

遍历 \(i\Leftrightarrow x\) 和 \(i\Leftrightarrow y\) 的min,如果距离大于ans,直接排除即可。由于点的范围远远小于边的长度,因此剪枝有效

为边按照长度递减排一次序。sjb大佬指点的,感觉有道理

嗯,思路就是这样,还算理清了。

在考场的时候我用模拟退火玄学调了调,居然骗到了70分(。・∀・)ノ

事后,加上了正解里的剪枝,再配上liamaidi“分锅炖”的惊奇思路,在不断地调火温,总算是玄学地炖熟了!

code:

#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std; struct data{
int a,b; double w; bool operator < (const data &x) const{
return w<x.w;
}
}e[20005]; const int MAX=305,INF=0x3f3f3f3f;
const double INFF=21474836472147483647.0,eps=1e-10; int n,m;
double d[MAX][MAX];
double ans=INFF;
clock_t st,ed; int ecnt; inline int read();
double simulatedannealing(int);
double distance(int,int,double,double); int main(){
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
#endif #ifdef ONLINE_JUDGE
freopen("WA.in","r",stdin);
freopen("WA.out","w",stdout);
#endif n=read(); m=read();
for(register int i=1;i<=n;++i) for(register int j=1;j<=n;++j) d[i][j]=INF;
for(register int i=1;i<=m;++i){
int a=read(),b=read(); double w; scanf("%lf",&w);
d[a][b]=d[b][a]=w;
ecnt++;
e[ecnt].a=a; e[ecnt].b=b; e[ecnt].w=w;
} for(register int k=1;k<=n;++k){
for(register int i=1;i<=n;++i){
for(register int j=1;j<=n;++j){
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
}
}
for(register int i=1;i<=n;++i) d[i][i]=0.0; sort(e+1,e+ecnt+1); for(register int k=1;k<=m;++k){
if(e[k].w>d[e[k].a][e[k].b]) continue;
if(e[k].w>ans*2) continue;
for(register int i=1;i<=n;++i) if(max(d[e[k].a][i],d[e[k].b][i])>=ans) continue;
ans=min(ans,simulatedannealing(k));
} printf("%lf\n",ans); return 0;
} inline int read(){
char tmp=getchar(); int sum=0; bool flag=false;
while(tmp<'0'||tmp>'9'){
if(tmp=='-') flag=true;
tmp=getchar();
}
while(tmp>='0'&&tmp<='9'){
sum=(sum<<1)+(sum<<3)+tmp-'0';
tmp=getchar();
}
return flag?-sum:sum;
} double simulatedannealing(int k){
double T=100.0,down=0.9;
double x=e[k].w/2,ans=distance(e[k].a,e[k].b,x,e[k].w); while(T>eps){
double nx=x+(rand()*2-RAND_MAX)/(double)RAND_MAX*e[k].w*T;
while(nx>e[k].w||nx<0.0) nx=x+(rand()*2-RAND_MAX)/(double)RAND_MAX*e[k].w*T;
double nans=distance(e[k].a,e[k].b,nx,e[k].w); if(nans<ans){
ans=nans; x=nx;
}
else if(exp((ans-nans)/T)*RAND_MAX>rand()){
ans=nans; x=nx;
} T*=down;
} if(m<1000) return ans; // lia mai di you hua
T=100.0;
while(T>eps){
double nx=x+(rand()*2-RAND_MAX)/(double)RAND_MAX*e[k].w*T;
while(nx>e[k].w||nx<0.0) nx=x+(rand()*2-RAND_MAX)/(double)RAND_MAX*e[k].w*T;
double nans=distance(e[k].a,e[k].b,nx,e[k].w); if(nans<ans){
ans=nans; x=nx;
}
else if(exp((ans-nans)/T)*RAND_MAX>rand()){
ans=nans; x=nx;
} T*=down;
} return ans;
} double distance(int a,int b,double w,double len){
double tmp=0.0;
for(int i=1;i<=n;++i){
tmp=max(tmp,min(d[i][a]+w,d[i][b]+len-w));
}
return tmp;
}

后记

第二次考试,感觉每一次考试都这么整理一下,肯定会累死受益匪浅

为了noip,继续努力

YMOI 2019.6.8的更多相关文章

  1. 2019年台积电进军AR芯片,将用于下一代iPhone

    近日,有报道表示台积电10nm 芯片可怜的收益率可能会对 2017 年多款高端移动设备的推出产生较大的影响,其中自然包括下一代 iPhone 和 iPad 机型.不过,台积电正式驳斥了这一说法,表明1 ...

  2. VS经常报错的link error 2019

    VS经常报错的link error 2019 原因如下: 可能是找得到头文件,但是相关的dll或者lib找不到,需要在配置里面添加相应的库文件. project=>configuration.. ...

  3. YTU 2019: 鞍点计算

    2019: 鞍点计算 时间限制: 1 Sec  内存限制: 64 MB 提交: 66  解决: 30 题目描述 找出具有m行n列二维数组Array的"鞍点",即该位置上的元素在该行 ...

  4. Windows Server 2019 预览版介绍

    在Windows server 2012.Windows server 2016还未完全普及的情况下,昨天Windows Server团队宣布Windows Server 2019将在2018年的下半 ...

  5. Telerik控件集-2019.R1.SP1.All

    Telerik 专注于微软.Net平台的表示层与内容管理控件,提供高度稳定性和丰富性能的组件产品DevCraft,并可应用在非常严格的环境中.Telerik拥有 Microsoft, HP, Alco ...

  6. CTF丨2019互联网安全城市巡回赛·西安站,我们来了!

    万物互联时代,网信事业发展突飞猛进,互联网悄然渗透到国民生活的每一个角落,伴随而来的网络安全威胁和风险也日渐突出.网络诈骗.钓鱼软件.勒索病毒等安全问题层出不穷,信息泄露等网络安全事件也频繁上演,给用 ...

  7. AI2(App Inventor 2)离线版服务器(2019.04.28更新)

    我们的目标:搭建一个本地多用户的App Inventor 2 服务器   演示: http://ai2.fsyz.net  [旧 win]     http://ai2n.fsyz.net [新 Ce ...

  8. Adobe Photoshop CC 2019 for Mac v20.0.4 中文版安装教程

    全新Adobe Photoshop CC 2019 mac特别版终于上线了,简称ps cc 2019,Adobe Photoshop CC 2019 for Mac v20.0.4 中文版安装教程分享 ...

  9. Python全国二级等级考试(2019)

    一.前言 2018年9月随着全国计算机等级考试科目中加入“二级Python”,也确立了Python在国内的地位,猪哥相信Python语言势必会像PS那般普及.不久的将来,谁会Python谁就能获得女神 ...

  10. Visual Studio 2019 发布活动 - 2019 年 4 月 2 日

    Visual Studio 2019 发布活动 2019 年 4 月 2 日,星期二 | 上午 9:00 (PT) 围观: https://visualstudio.microsoft.com/zh- ...

随机推荐

  1. 痞子衡嵌入式:i.MXRT中FlexSPI外设不常用的读选通采样时钟源 - loopbackFromSckPad

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是i.MXRT中FlexSPI外设不常用的读选通采样时钟源 - loopbackFromSckPad. 最近碰到一个客户,他们在 i.MX ...

  2. 浅谈消息队列 Message Queue

    消息队列:在消息传递的过程中暂时保存消息的容器,充当发送者和接受者的中间人 消息队列的基本操作 using System; using System.Messaging; namespace MQ { ...

  3. IO模型及高性能网络架构分析

    前言 操作系统一次IO调用过程 应用程序发起的一次IO操作包含两个阶段: IO调用:应用程序进程向操作系统内核发起调用. IO执行:操作系统内核完成IO操作. 操作系统内核完成IO操作还包括两个过程: ...

  4. JS逆向实战4--cookie——__jsl_clearance_s 生成

    分析 网站返回状态码521,从浏览器抓包来看,浏览器一共对此地址请求了三次(中间是设置cookie的过程): 第一次请求:网站返回的响应状态码为 521,响应返回的为经过 混淆的 JS 代码:但是这些 ...

  5. Python基础之函数:1、函数的介绍及名称空间

    目录 一.函数 1.什么是函数 2.函数的语法结构 3.函数的定义与调用 4.函数的分类 5.函数的返回值 6.函数的参数 二.函数参数 1.位置参数 2.默认参数 3.可变长参数 1.一个*号 2. ...

  6. 嵌入式-Linux基础操作

    Crtl+Alt+T:调出命令窗口 xrandr:列出分辨率列表 设置窗口的分辨率大小为1280x960:xrandr -s 1280x960 通过命令窗口来执行一段C语言程序: VI工具的使用: ( ...

  7. Java 同步锁ReentrantLock与抽象同步队列AQS

    AbstractQueuedSynchronizer 抽象同步队列,它是个模板类提供了许多以锁相关的操作,常说的AQS指的就是它.AQS继承了AbstractOwnableSynchronizer类, ...

  8. 2022春每日一题:Day 23

    题目:Piotr's Ants 蚂蚁转头走,其实可以看做他们交换灵魂后接着往前走,同样发现,既然他们的速度相同,那么在数轴上相对位置不会改变(碰面会改变方向),那就好办了. 先把初始状态排序,id都记 ...

  9. mindxdl--common--logger.go

    // Copyright (c) 2021. Huawei Technologies Co., Ltd. All rights reserved.// Package common the contr ...

  10. 09 | 从容器到容器云:谈谈Kubernetes的本质

    你好,我是张磊.今天我和你分享的主题是:从容器到容器云,谈谈Kubernetes的本质. 在前面的四篇文章中,我以Docker项目为例,一步步剖析了Linux容器的具体实现方式.通过这些讲解你应该能够 ...