APIO2017伪题解
题目质量还是比较高的,只是当时澳大利亚方面出了一点问题?最后造成了区分度非常迷的局面。
纵观三道题,T1是披着交互外衣的提答题,T2是披着交互外衣的传统题,T3是一道据说近年来APIO最水的一道传统题,然而对于不敢相信T3水的选手来说只能靠硬搞两道交互拿分了。
T1 Rainbow
没脸写题解和程序了。
Subtask 1直接floodfill一下,Subtask 2听说前缀和讨论一下,后面听说数据结构维护一下。
T2 Koala
这个感觉可以写一篇交互库玩耍记了,感觉前几个部分分就是在考调戏交互库的水平。
首先看Subtask 1只有4分,而且场上超过300人拿了这4分,但是我不会怎么办?
猜一个结论,随便选一个旁边放一个宝石,其它都不放,然后koala没放宝石的那个就是最小的那个。就过了。(我竟然没想到)
然后如果光搞Subtask 2感觉想得出结论会比较吃力,因为这题实际上是一道构造题。这个时候就需要在交互库上做文章了。
看一会代码发现交互库实际上就是一个无脑DP(于是就可以确定交互库占用的时空复杂度了),并没有什么可以利用的地方。但是我们可以把它改成一个能够做实验的程序,可供我们猜结论使用,如下
#include<cstdio>
#include<ctime>
#include<algorithm>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
using namespace std; int N,W,P[],b[],r[],c[]; void playRound(int *B, int *R) {
int i, j; int cache[][];
int num[][];
char taken[][]; for (i=;i<;++i) {
cache[][i] = ;
num[][i] = ;
} for (i=;i<N;++i) {
int v = B[i]+;
int ii = i&;
int o = ii^;
for (j=;j<=W;++j) {
cache[ii][j] = cache[o][j];
num[ii][j] = num[o][j];
taken[i][j] = ;
}
for (j=W;j>=v;--j) {
int h = cache[o][j-v] + P[i];
int hn = num[o][j-v] + ;
if (h > cache[ii][j] || (h == cache[ii][j] && hn > num[ii][j])) {
cache[ii][j] = h;
num[ii][j] = hn;
taken[i][j] = ;
} else {
taken[i][j] = ;
}
}
} int cur = W;
for (i=N-;i>=;--i) {
R[i] = taken[i][cur] ? (B[i] + ) : ;
cur -= R[i];
}
} int main(){
srand(time(NULL));
N=; W=;
for (int i=; i<N; i++){
int k=rand()%N+; while (c[k]) k=rand()%N+;
c[k]=; P[i]=k;
} b[]=b[]=;
for (int i=; i<; i++) b[i]=; playRound(b,r);
for (int i=; i<N; i++){
printf("%3d%3d ",i,r[i]);
if ((i+)%==) puts("");
}
return ;
}
对于如何分配b数组,只需要改主函数里的两句话即可。
通过实验发现,现在已经确定的区间为[1,100],如果每个位置放一个的话,就可以确定[51,100],然后这部分放2个其余都不放的话可以确定[76,100],这部分再放4个其余不放的话可以确定[92,100],这部分放11个可以确定[100,100]。
这样我们真好用了4次确定了100的位置。
考虑如何测试,写一个数据生成程序:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<sys/timeb.h>
#include<cstdlib>
#include<fstream>
#define rep(i,l,r) for (int i=(l); i<=(r); i++)
typedef long long ll;
using namespace std; ofstream fout,fres; const int N=;
int b[N]; void insd(){
struct timeb tp; ftime(&tp);
srand(tp.time*+tp.millitm);
} int sj(int l,int r){ return rand()%(r-l+)+l; } int main(){
insd();
fout.open("koala.in"); fres.open("result");
ios::sync_with_stdio(false); int F=sj(,),G=sj(,);
fout<<F<<' '<<G<<endl;
if (F==){
while (G--){
fout<<"100 100 ";
rep(i,,) b[i]=;
rep(i,,){
int k=sj(,); while (b[k]) k=sj(,);
fout<<k<<' '; b[k]=;
if (k==) fres<<i<<endl;
}
fout<<endl;
}
}else if (F==){
while (G--){
fout<<"100 100 ";
rep(i,,) b[i]=;
rep(i,,){
int k=sj(,); while (b[k]) k=sj(,);
fout<<k<<' '; b[k]=;
if (k==) fres<<i<<endl;
}
fout<<endl;
}
}else if (F==){
while (G--){
int x=,y=;
fout<<"100 100 ";
rep(i,,) b[i]=;
rep(i,,){
int k=sj(,); while (b[k]) k=sj(,);
fout<<k<<' '; b[k]=;
if (!x) x=k; else if (!y) fres<<((k>x)?:)<<endl,y=k;
}
fout<<endl;
}
}
fout.close(); fres.close();
return ;
}
这份代码在生成数据的同时将答案输出到了result文件里,方便对拍。对拍的代码:
g++ make.cpp -o make -g -Wall
g++ koala.cpp grader.cpp -o koala -static -std=c++
while true; do
echo ---------------------
./make
time ./koala<koala.in>koala.out
if !(diff -w koala.out result); then exit; fi
done
现在看Subtask 3,用上面的程序实验发现,其余都不放,如果b[0]和b[1]都放15的话,两个都不会被选。这样我们在[1,15]内二分,每次保证b[0]=b[1],直到发现有一个选了而另一个没选就可以返回答案了。
这是C<=4的算法,能拿14分,满分是将区间人工细化以减小一次机器搜索。
前33分程序:
#include "koala.h"
#include<cstdio>
using namespace std; const int N=;
int b[N],r[N],c[N]; int minValue(int N, int W) {
for (int i=; i<N; i++) b[i]=; b[]=;
playRound(b,r);
for (int i=; i<N; i++) if (!r[i]) return i;
return ;
} int maxValue(int N, int W) {
for (int i=; i<N; i++) b[i]=,c[i]=;
playRound(b,r);
for (int i=; i<N; i++) if (!r[i]) c[i]=;
for (int i=; i<N; i++) if (!c[i]) b[i]=; else b[i]=;
playRound(b,r);
for (int i=; i<N; i++) if (r[i]!=) c[i]=;
for (int i=; i<N; i++) if (!c[i]) b[i]=; else b[i]=;
playRound(b,r);
for (int i=; i<N; i++) if (r[i]!=) c[i]=;
for (int i=; i<N; i++) if (!c[i]) b[i]=; else b[i]=;
playRound(b,r);
for (int i=; i<N; i++) if (r[i]==) return i;
return ;
} int greaterValue(int N, int W) {
int L=,R=;
while (L<R){
int mid=(L+R)>>;
b[]=b[]=mid;
for (int i=; i<N; i++) b[i]=; playRound(b,r); if (!r[] && r[]) return ;
if (!r[] && r[]) return ; if (!r[] && !r[]) R=mid-; else L=mid+;
}
return L;
} void allValues(int N, int W, int *P) { if (W == *N) { } else { } }
Subtask 4是给数组排序,发现如果b[x]和b[y]各放100个的话,肯定有一个被选而另一个不选,也就是说可以用一次游戏完成两个数的比较。
所以我们手工实现一个$O(n\log n)$的比较排序算法即可。
或许可以直接写进std::sort的cmp里,没试过。
Subtask 5没做。
T3 merchant
首先第一眼分数规划,第二眼spfa判负环,问题解决了一半。
然后发现因为每次买卖涉及两个城市,所以一般做法没办法给每条边赋值。
考虑根据决策重建图,如果两个点u和v又一次买卖,一定是u的买入价和v的卖出价中差值最大的那个,所以根据这个连边,构成一个边数$n^2$的图,就可以直接跑spfa了。
比较坑的情况是一个城市同一个商品的卖出价可能大于买入价,也就是说留在这里不走可以一直赚钱。但题目显然是不允许这样的。
#include<cstdio>
#include<algorithm>
#define rep(i,l,r) for (int i=l; i<=r; i++)
typedef long long ll;
using namespace std; const int N=,M=,inf=;
int n,m,K,u,v,w,mx,flag,mid,vis[N],val[N][N],mp[N][N],a[N][M],b[N][M];
ll d[N]; void spfa(int x){
if (flag) return; vis[x]=;
rep(i,,n) if (i!=x && mp[x][i]<inf && d[i]>d[x]+1ll*mp[x][i]*mid-val[x][i]){
d[i]=d[x]+1ll*mp[x][i]*mid-val[x][i];
if (vis[i]) { flag=; return; } else spfa(i);
}
vis[x]=;
} int jud(int mid){
rep(i,,n) vis[i]=d[i]=; flag=;
rep(i,,n){ spfa(i); if (flag) return ; }
return ;
} int main(){
freopen("merchant.in","r",stdin);
freopen("merchant.out","w",stdout);
scanf("%d%d%d",&n,&m,&K);
rep(i,,n) rep(j,,K) scanf("%d%d",&a[i][j],&b[i][j]),mx=max(mx,b[i][j]);
rep(i,,n) { rep(j,,n) mp[i][j]=inf; mp[i][i]=; }
rep(i,,m) scanf("%d%d%d",&u,&v,&w),mp[u][v]=min(mp[u][v],w);
rep(k,,n) rep(i,,n) rep(j,,n) mp[i][j]=min(mp[i][j],mp[i][k]+mp[k][j]);
rep(i,,n) rep(j,,n) rep(k,,K)
if (i!=j && ~a[i][k] && ~b[j][k]) val[i][j]=max(val[i][j],b[j][k]-a[i][k]);
int L=,R=mx,ans=;
while (L<=R){
mid=(L+R)>>;
if (jud(mid)) ans=mid,L=mid+; else R=mid-;
}
printf("%d\n",ans);
return ;
}
APIO2017伪题解的更多相关文章
- 伪题解 洛谷 P1363 幻想迷宫(DFS)
毒瘤题,做了一晚上抄题解A了 因为是抄题解,我也不好意思说什么了,就发篇博客纪念一下吧 #include<iostream> #include<cstring> #includ ...
- WC2018伪题解
NOIP分数过低的场外选手,一个月之后才有幸膜到这套卷子.感觉题目质量很不错啊,可惜了T1乱搞可过,T2题目出锅非集训队员没有通知到,导致风评大幅被害. 感觉Cu的话随手写两个暴力就稳了,Ag的话T3 ...
- BZOJ4898 & BZOJ5367 & 洛谷3778:[APIO2017]商旅——题解
https://www.lydsy.com/JudgeOnline/problem.php?id=4898 https://www.lydsy.com/JudgeOnline/problem.php? ...
- [CTSC2017]最长上升自序列(伪题解)(Dilworth's theorem+网络流)
部分分做法很多,但每想出来一个也就多5-10分.正解还不会,下面是各种部分分做法: Subtask 1:k=1 LCS长度最长为1,也就是说不存在j>i和a[j]>a[i]同时成立.显然就 ...
- APIO2018练习赛伪题解
传送门:https://pcms.university.innopolis.ru/statements/org/apio/2018/practice/statements.pdf 主要就在于后面三道构 ...
- 【伪题解】 [Offer收割]编程练习赛58
[A:最大的K-偏差排列]: 第一次在hiho卡一题,所以暴力了搜索了一下,70分,后面回来打表找规律,规律是有和K有关的周期. 当K<=N/2时,成周期交叉变化,最后尾部部分单独考虑. 当K& ...
- 洛谷 P3952时间复杂度 (本地AC测评RE的伪题解)
[题目描述] 小明正在学习一种新的编程语言 A++,刚学会循环语句的他激动地写了好多程序并 给出了他自己算出的时间复杂度,可他的编程老师实在不想一个一个检查小明的程序, 于是你的机会来啦!下面请你编写 ...
- PKUSC2018训练日程(4.18~5.30)
(总计:共66题) 4.18~4.25:19题 4.26~5.2:17题 5.3~5.9: 6题 5.10~5.16: 6题 5.17~5.23: 9题 5.24~5.30: 9题 4.18 [BZO ...
- AHOI2018训练日程(3.10~4.12)
(总计:共90题) 3.10~3.16:17题 3.17~3.23:6题 3.24~3.30:17题 3.31~4.6:21题 4.7~4.12:29题 ZJOI&&FJOI(6题) ...
随机推荐
- 【BZOJ 3316】JC loves Mkk 01分数规划+单调队列
单调栈不断吞入数据维护最值,数据具有单调性但不保证位置为其排名,同时可以按照进入顺序找出临近较值单调队列队列两端均可删除数据但只有队末可以加入数据,仍然不断吞入数据但同时可以额外刨除一些不符合条件的数 ...
- [fzu 2271]不改变任意两点最短路至多删的边数
题目链接:http://acm.fzu.edu.cn/problem.php?pid=2271 题目中说每条边的边权都是[1,10]之间的整数,这个条件非常关键!以后一定要好好读题啊…… 做10次循环 ...
- Failed to resolve:com.android.support:appcompat-v7
http://blog.csdn.net/mhl18820672087/article/details/78385361
- MFC 对话框透明效果
网上找的资料自己改了改,在这里记录和分享一下,主要是TransparentWnd函数. 在子类的OnShowWindow函数中调用 ShowWindowAlpha() #pragma once tem ...
- Patch Windows with SSM on AWS
ec2ssmupdate https://docs.amazonaws.cn/systems-manager/latest/userguide/systems-manager-patch.htmlht ...
- [BZOJ1982][POJ1740][Spoj 2021]Moving Pebbles|解题报告
这道题的题意BZ和POJ上的都不大清楚... 大概就是给出n堆石子,以及初始每堆石子的个数 两个玩家交替操作,每个操作可以任意在一堆中取任意多的石子 然后再从这堆里拿若干个石子放到某个当前还存在的堆里 ...
- 【CF24D】Broken Robot (DP+高斯消元)
题目链接 题意:给定一个\(n\times m\)的矩阵,每次可以向→↓←移动一格,也可以原地不动,求从\((x,y)\)到最后一行的期望步数. 此题标签\(DP\) 看到上面这个肯定会想到 方法一: ...
- [bzoj3238][Ahoi2013]差异——后缀自动机
Brief Description Algorithm Design 下面给出后缀自动机的一个性质: 两个子串的最长公共后缀,位于这两个串对应的状态在parent树上的lca状态上.并且最长公共后缀的 ...
- [bzoj3277==bzoj3473]出现k次子串计数——广义后缀自动机+STL
Brief Description 给定n个字符串,对于每个字符串,您需要求出在所有字符串中出现次数大于等于k次的子串个数. Algorithm Design 先建立一个广义后缀自动机,什么是广义后缀 ...
- java List排序 顺序 倒序 随机
List list = new LinkedList(); for ( int i = 0 ; i < 9 ; i ++ ) { list.add( " a " + i); ...