题解 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. python关于Django搭建简单博客项目 详解二-setting.py

    这一篇我们来讲解setting.py,具体内容以注释形式写入到下面的setting.py代码中,篇幅所限已把官方所给英文注释删除. 全部源代码和详解请参看http://github.com/Cheng ...

  2. 44.drf缓存

    DRF原有缓存 Django缓存.配置:https://www.cnblogs.com/Mickey-7/p/15792083.html   Django为基于类的视图提供了一个 method_dec ...

  3. C# String.IsNullOrEmpty()方法的使用

    IsNullOrEmpty(string)是String类的一个有参的方法,方法需要类的调用,所以String.IsNullOrEmpty(string) IsNullOrEmpty是判断字符串的Nu ...

  4. Linux之Docker-01

    一.镜像基础命令 1.docker version  [root@DY-Ubuntu-01 ~]#docker version               #查看 Docker 版本 2.docker ...

  5. JS逆向实战8——某网实战(基于golang-colly)

    其实本章算不上逆向教程 只是介绍golang的colly框架而已 列表页分析 根据关键字搜索 通过抓包分析可知 下一页所请求的参数如下 上图标红的代表所需参数 所以其实我们真正需要的也就是Search ...

  6. disk磁盘分区软件使用教程,磁盘扩容无损备份

    前几天,因为我的笔记本电脑C盘D盘全红了,趁着双11固态降价,赶紧买了一张三星980 500g 给我的拯救者插上了,加上原来的500g,总共1T,已经够用了. 不得不说拯救者系列预留的1个M.2固态插 ...

  7. python枚举类型 Enum

    在python中枚举是一种类(Enum) 枚举类中不能存在相同的标签名 枚举是可迭代的 例: from enum import Enum class Vip(Enum): MONDAY = 0 TUE ...

  8. 高性能MySQL(第4版) 第一章 MySQL架构 读书笔记

    这本书去年11月出的,今年中文版也出了,并且直接上了微信读书,之后有空就读一读,分享下读书笔记~ 原文内容比较充实,建议有时间可以读一下原文. 第一章主要是个概览. MySQL的逻辑架构 默认情况下, ...

  9. 嵌入式-C语言基础:字符串比较函数strcmp及其实现

    #include<stdio.h> #include <string.h> int mystrcmp(char * p1,char * p2) { int ret=0; if( ...

  10. 【Java并发011】原理层面:CAS操作全解析

    一.前言 volatile关键字是Java51个关键字中用的比较少的一个,它是一个与多线程并发的关键字,但是实际开发中,一般不会用到,使用synchronize+wait()+notify()/not ...