题解 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基本数据类型

    一. 字面量 代码中,被写在代码中的固定的值,称之为字面量 Python常用6种值(数据)类型 字符串(string) :又称文本,是由任意数量的字符如中文.英文.各类符号.数字等组成.所以叫做字符的 ...

  2. Docker在windows系统以及Linux系统的安装

    Docker简介和安装 Docker是什么 Docker 是一个应用打包.分发.部署的工具 你也可以把它理解为一个轻量的虚拟机,它只虚拟你软件需要的运行环境,多余的一点都不要, 而普通虚拟机则是一个完 ...

  3. 云原生之旅 - 6)不能错过的一款 Kubernetes 应用编排管理神器 Kustomize

    前言 相信经过前一篇文章的学习,大家已经对Helm有所了解,本篇文章介绍另一款工具 Kustomize,为什么Helm如此流行,还会出现 Kustomize?而且 Kustomize 自 kubect ...

  4. .NET Conf 2022 &ndash; 11 月 8 日至 10 日

    .NET Conf 2022 下周就正式开启了,时间是美国时间的 11月8日至10日..NET Conf 2022是一个免费的,为期三天的, 虚拟开发人员活动提供多种实时会话,其中包括来自社区和 .N ...

  5. Mybatis:解决调用带有集合类型形参的mapper方法时,集合参数为空或null的问题

    此文章有问题,待修改! 使用Mybatis时,有时需要批量增删改查,这时就要向mapper方法中传入集合类型(List或Set)参数,下面是一个示例. // 该文件不完整,只展现关键部分 @Mappe ...

  6. FIT软件开发

    1.baidu,google 术和道 2.FIT: future integrated Technology 3.集体检视 > commiter 4.高内聚,低耦合 => 太极 => ...

  7. Go语言核心36讲42-----io包中接口的好处与优势

    我们在前几篇文章中,主要讨论了strings.Builder.strings.Reader和bytes.Buffer这三个数据类型. 知识回顾 还记得吗?当时我还问过你"它们都实现了哪些接口 ...

  8. 【Serverless】Unity快速集成认证服务实现邮件登录

    ​概述: 认证服务可以为您的应用快速构建安全可靠的用户认证系统,您只需在应用中访问认证服务的相关能力,而不需要关心云侧的设施和实现. 本次将带来如何使用Unity编辑器快速集成认证服务SDK并实现邮箱 ...

  9. linux系统移植

    1 linux环境搭建 1.1 添加交叉开发工具链 新建如下工程目录: gcc-4.6.4.tar.xz #拷贝 tar -Jxvf gcc-4.6.4.tar.xz #解压 cd ./gcc-4.6 ...

  10. 关于解决linux python3安装gattlib报错

    1. 报错信息 /usr/bin/ld: cannot find -lboost_python36 collect2: error: ld returned 1 exit status error: ...