20211007
Posted JA2012
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了20211007相关的知识,希望对你有一定的参考价值。
AM
预期 | 实际 | \\(\\Delta\\) | |
---|---|---|---|
A | 30 | 10 | -20 |
B | 100 | 100 | 0 |
C | 100 | 100 | 0 |
D | 100 | 50 | -50 |
E | 100 | 100 | 0 |
F | 100 | 100 | 0 |
总计 | 530 | 460 | -70 |
A Sightseeing Cows
题目描述
给定一张 L 个点、P条边的有向图,每个点都有一个权值 ,每条边都有一个权值 。
求图中的一个环,使“环上各点的权值之和”除以“环上各边的权值之和”最大。
输出这个最大值。
样例输入
5 7
30
10
10
5
10
1 2 3
2 3 2
3 4 5
3 5 2
4 5 5
5 1 3
5 2 2
样例输出
6.00
做法
这是我一直忽视没有看的一个part—0/1分数规划
0/1分数规划的模型是:给定\\(a_1,\\cdots,a_n\\),\\(b_1,\\cdots,b_n\\),求一组解\\(x_i(1\\leq i\\leq n,x_i\\in\\{0,1\\})\\)使下式最大化:(说法来自算进)
做法是二分答案
对于二分的值mid,如果\\(mid\\leq ans\\)
反之则左式一定为负,故可以用左式最大值是否非负作为check函数判定条件
那么对于这道题,设点权为f,边权为g,我们也可用\\(\\sum f_v-k*g_{e(u\\rightarrow v)}\\)最大值是否非负做判定条件,但这样还是麻烦
所以换个表达方式,求\\(\\sum k*g_{e(u\\rightarrow v)}- f_v\\)最小值是否为负,而且这道题要找的是环,那么就是求图中有没有负环
找负环.....那么方法就呼之欲出了——二分后更新所有边权,然后在新图上跑SPFA,根据SPFA的性质,如果被更新了N次就会有负环
\\(\\cal code:\\)
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const double eps=1e-5;
int L,T;
int f[1010];//点权
struct edge{
int next,to,val;
}e[5010];
int head[1010],cnte,cnt[1010];
bool v[1010];
queue<int>q;
double r=2000,l=0,mid,d[1010],G[5010];//G是更新后的边权
void add(int f,int t,int cost){
e[++cnte]=(edge){head[f],t,cost};
head[f]=cnte;
}
void change(double data){
for(int i=1;i<=cnte;i++)
G[i]=data*e[i].val-f[e[i].to];
}
bool find_cir(){
//spfa
memset(v,0,sizeof(v));
memset(cnt,0,sizeof(cnt));
for(int i=0;i<=L;i++)
d[i]=1000000.0;
d[1]=0;
v[1]=1;
q.push(1);
while(q.size()){
int x=q.front();q.pop();
v[x]=0;
for(int i=head[x];i;i=e[i].next){
int y=e[i].to;
double va=G[i];
if(d[y]>d[x]+va){
d[y]=d[x]+va;
cnt[y]=cnt[x]+1;
if(cnt[y]==L) return 1;
if(!v[y]){
v[y]=1;
q.push(y);
}
}
}
}
return 0;
}
int main(){
scanf("%d%d",&L,&T);
for(int i=1;i<=L;i++)
scanf("%d",&f[i]);
for(int i=0;i<T;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
while(r-l>eps){
mid=(r+l)/2;
change(mid);
if(find_cir()){
l=mid;
}
else
r=mid;
}
printf("%.2lf",mn);
return 0;
}
B 升降梯上
题目描述
开启了升降梯的动力之后,探险队员们进入了升降梯运行的那条竖直的隧道,映入眼帘的是一条直通塔顶的轨道、一辆停在轨道底部的电梯、和电梯内一杆控制电梯升降的巨大手柄。
Nescafé 之塔一共有N 层,升降梯在每层都有一个停靠点。手柄有M 个控制槽,第i个控制槽旁边标着一个数Ci,满足C1<C2<C3<...<CM。如果Ci>0,表示手柄扳动到该槽时,电梯将上升Ci 层;如果Ci<0,表示手柄扳动到该槽时,电梯将下降-Ci 层;并且一定存在一个Ci=0,手柄最初就位于此槽中。注意升降梯只能在1~N 层间移动,因此扳动到使升降梯移动到1 层以下、N 层以上的控制槽是不允许的。
电梯每移动一层,需要花费2 秒钟时间,而手柄从一个控制槽扳到相邻的槽,需要花费1 秒钟时间。探险队员现在在1 层,并且想尽快到达N 层,他们想知道从1 层到N 层至少需要多长时间?
样例输入
6 3
-1 0 2
样例输出
19
做法
就是BFS
\\(\\cal code:\\)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=1005,M=25,INF=0x3f3f3f3f;
struct pos{
int floor,id,tm;
}q[N*N];
int h=1,t,n,m,c[M],ans=INF,rec[N];//rec记录较优解在q中的编号
bool check(int fl,int tim,int c0,int c1){
if(fl+c[c1]<1||fl+c[c1]>n) return 0;
int newtime=tim+abs(c0-c1)+2*abs(c[c1]);
if((!rec[fl+c[c1]])||
newtime<q[rec[fl+c[c1]]].tm+abs(q[rec[fl+c[c1]]].id-c1)) return 1;
return 0;
}
void BFS(){
for(;h<=t;++h){
int now=q[h].floor,tim=q[h].tm,num=q[h].id;
for(int i=1;i<=m;++i){
if(check(now,tim,num,i)){
q[++t]=(pos){now+c[i],i,tim+abs(num-i)+2*abs(c[i])};
if(now+c[i]==n)ans=min(ans,q[t].tm);
if((!rec[now+c[i]])||q[t].tm<q[rec[now+c[i]]].tm)
rec[now+c[i]]=t;
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i){
scanf("%d",&c[i]);
if(c[i]==0) q[++t]=(pos){1,i,0};
}
BFS();
if(ans==INF) puts("-1");
else
printf("%d",ans);
return 0;
}
C 银河英雄传说
题目描述
最初有30000列长度为1的战舰队列,支持两种操作:
-
M i j 将第\\(i\\)号战舰所在队列整个接在第\\(j\\)号战舰所在队列后面
-
C i j 询问\\(i\\)号战舰与\\(j\\)之间隔了多少战舰(不在同一列输出-1)
样例输入
4
M 2 3
C 1 2
M 2 4
C 4 2
样例输出
-1
1
做法
带权并查集典型例题
\\(\\cal code:\\)
int fa[N],sz[N],T,d[N];char s[2];
int find(int x){
if(fa[x]==x) return x;
int fax=find(fa[x]);
d[x]+=d[fa[x]];//d是x到find(x)的距离
return fa[x]=fax;
}
int main(){
for(int i=1;i<N;++i) fa[i]=i,sz[i]=1;
scanf("%d",&T);
for(int i=1;i<=T;++i){
int x,y;
scanf("%s%d%d",s,&x,&y);
int fax=find(x),fay=find(y);
if(s[0]==\'M\'){
fa[fax]=fay;
d[fax]=sz[fay];
sz[fay]+=sz[fax];
}
else{
if(fax!=fay) puts("-1");
else//这里是担心询问的x=y输出负数,具体有没有我也不知道
printf("%d\\n",max(abs(d[x]-d[y])-1,0));
}
}
return 0;
}
D 导弹防御塔
题目描述
Freda控制着N座可以发射导弹的防御塔。每座塔都有足够数量的导弹,但是每座塔每次只能发射一枚。在发射导弹时,导弹需要T1秒才能从防御塔中射出,而在发射导弹后,发射这枚导弹的防御塔需要T2分钟来冷却。
所有导弹都有相同的匀速飞行速度V,并且会沿着距离最短的路径去打击目标。计算防御塔到目标的距离Distance时,你只需要计算水平距离,而忽略导弹飞行的高度。导弹在空中飞行的时间就是 (Distance/V) 分钟,导弹到达目标后可以立即将它击毁。
现在,给出N座导弹防御塔的坐标,M个入侵者的坐标,T1、T2和V,你需要求出至少要多少分钟才能击退所有的入侵者。
样例输入
3 3 30 20 1
0 0
0 50
50 0
50 50
0 1000
1000 0
样例输出
91.500000
做法
上次那个冷冻波是网络流+二分答案,这次的导弹防御塔有相似之处,做法是二分图最大匹配+二分答案
因为不方便直接求出最短用时,每座塔打几发,分别打谁也是不好下定论的,所以考虑二分用时
接下来考虑check
用时确定后,每座塔能发几发导弹就确定了,能打到谁也确定了,如果我们将每个导弹拆开(不然无法在每发导弹的用时方面达成区分功能,一座塔与多个敌人达成匹配也不利于判定)
那么现在情况就是图上的点分成两类(导弹&敌人),每类点内部不互相连接....这就是一张二分图
再看看check成功的条件:敌人全部被消灭,相当于就是达成了\\(m\\)组匹配,所以判定方法就是建图+跑最大匹配
F 最优乘车
题目描述
H城是一个旅游胜地,每年都有成千上万的人前来观光。为方便游客,巴士公司在各个旅游景点及宾馆,饭店等地都设置了巴士站并开通了一些单程巴士线路。每条单程巴士线路从某个巴士站出发,依次途经若干个巴士站,最终到达终点巴士站。
一名旅客最近到 H 城旅游,他很想去 S 公园游玩,但如果从他所在的饭店没有一路巴士可以直接到达 S 公园,则他可能要先乘某一路巴士坐几站,再下来换乘同一站台的另一路巴士, 这样换乘几次后到达 S 公园。
现在用整数 1,2,…N 给 H 城的所有的巴士站编号,约定这名旅客所在饭店的巴士站编号为 1,S 公园巴士站的编号为 N。
写一个程序,帮助这名旅客寻找一个最优乘车方案,使他在从饭店乘车到 S 公园的过程中换车的次数最少。
样例输入
3 7
6 7
4 7 3 6
2 1 3 5
样例输出
2
做法:建图
我其实....应该是建了一张分层图?
如果建图,边权的0/1其实不在于在哪个站,而在于是否存在转车的行为,所以只建一层图并不能正确的界定转车行为
我的方法简单来说就是拆点,把\\(x\\)站拆成1号线上的\\(x\\)站点,2号线上的\\(x\\)站点\\(\\dots\\) (即使有些线路不经过) 一条线路内各点边权为0,转车站台各线路之间边权为1,最后用各个1号点跑最短路取最小值
(事后参考了zyc的方法,可以虚拟一个0号点向各个一号点建边,虚拟一个\\(n+1\\)号点向各个\\(n\\)号点连边,这样应该可以只跑一遍最短路)
PM TJOI2013
预期 | 实际 | \\(\\Delta\\) | |
---|---|---|---|
A | 30 | 30 | 0 |
B | 100 | 100 | 0 |
C | 100 | 70 | -30 |
A 松鼠聚会
题目描述
草原上住着一群小松鼠,每个小松鼠都有一个家。时间长了,大家觉得应该聚一聚。但是草原非常大,松鼠们都很头疼应该在谁家聚会才最合理。
每个小松鼠的家可以用一个点 (x,y)表示,两个点的距离定义为点 (x,y)和它周围的 8个点 (x−1,y),(x+1,y),(x,y−1),(x,y+1),(x−1,y+1),(x−1,y−1),(x+1,y+1),(x+1,y−1) 距离为 1。
求松鼠为聚会走的最小路程和
样例输入
6
-4 -1
-1 -2
2 -4
0 2
0 3
5 -2
样例输出
20
做法
暴力枚举\\(O(n^2)\\)只有30pts
先把距离的定义翻译成人话,就是\\(dis=\\max(\\Delta x,\\Delta y)\\),是所谓的切比雪夫距离
然后我们还有一个熟悉的曼哈顿距离:\\(dis=\\Delta x+\\Delta y\\)
两个形式有相似之处,实际上也是可以互相转化的,以下为推导过程:
如果令\\(x_1\'=x_1+y_1,y_1\'=x_1-y_1,x_2\'=x_2+y_2,y_2\'=x_2-y_2\\),该表达式与切比雪夫距离表达式完全一致
因此,切比雪夫意义下的坐标\\((x,y)\\)可以转化为曼哈顿意义下\\((x+y,x-y)\\),曼哈顿意义下的坐标\\((x,y)\\)可以转化为切比雪夫意义下的\\((\\frac{x+y}{2},\\frac{x-y}{2})\\),本题中为了方便可以先不除2,最后输出答案再除2
上面的转化保证了我们可以将接下来就可以做了
以x为例,我们化简一下式子
于是就可以轻松的用前缀和搞定了,至于i的找法,可以用lower_bound
B 拯救小矮人
题目描述
一群小矮人掉进了一个很深的陷阱里,由于太矮爬不上来,于是他们决定搭一个人梯。即:一个小矮人站在另一小矮人的 肩膀上,知道最顶端的小矮人伸直胳膊可以碰到陷阱口。
对于每一个小矮人,我们知道他从脚到肩膀的高度Ai,并且他的胳膊长度为Bi。陷阱深度为H。
如果我们利用矮人1,矮人2,矮人3,。。。矮人k
搭一个梯子,满足A1+A2+A3+....+Ak+Bk>=H,那么矮人k就可以离开陷阱逃跑了,一旦一个矮人逃跑了,他就不能再搭人梯了。
我们希望尽可能多的小矮人逃跑,问最多可以使多少个小矮人逃跑。
第一行一个整数N, 表示矮人的个数,
接下来N行每一行两个整数Ai和Bi,最后一行是H。(Ai,Bi,H<=10^5)
样例输入
2
20 10
5 5
30
样例输出
2
做法:DP
第一想法:贪心
初步想法是把需求高度高,身高低的先放出去,但是对于需求高身高高的或长臂猿形人才不好处理,怎么搞都有反例
最终还是没能选贪心,所以第二想法DP
想到DP后先想状态,很容易能想到\\(f_{i,j}\\)表示考虑到第\\(i\\)个人,出去了\\(j\\)个人后少掉的最低高度(出去的人的最小高度和),转移式如下
蒟蒻习惯先写再压位,上式显然是个背包,压维方式是很常规的倒序枚举,不再赘述
最长上升子序列
题目描述
给定一个序列,初始为空。现在我们将1到N的数字插入到序列中,每次将一个数字插入到一个特定的位置。每插入一个数字,我们都想知道此时最长上升子序列长度是多少?
样例输入
3
0 0 2
样例输出
1
1
2
做法
虽说\\(vector\\)自带插入功能,\\(rope\\)代码巨短,但我都不会用...所以写个树状数组+线段树的题解
part 1:找序列
这部分参考buy tickets的做法,因为最后一个人插队的位置必然是空位中的对应排名,可以倒序处理
至于找空格第\\(k\\)格,可以树状数组+二分查找,以下代码
int find(int val){
int l=0,r=n,mid=(l+r)/2,res=0;
while(l<=r){//二分写的比较拉,见谅
mid=(l+r)/2;
if(ask(mid)>=val) res=mid,r=mid-1;
else l=mid+1;
}
return res;
}
....
for(int i=n;i;--i){
int k=find(pos[i]);
pos[i]=k;
add(k,-1);//初始会所有数置为1
}
part 2 找最长上升子序列
一般而言写的都是\\(O(n^2)\\)的算法,现在需要优化成\\(O(\\log n)\\)
- 依序考虑每个数,建一个栈,如果这个数大于栈顶元素直接入栈,否则找到大于自己的第一个元素替换(二分),最后栈中元素个数为答案
这个方法虽然也还行,但需要不断重新扫整个数列,实测T7个点(加一个优化可以T3个点)
- 依旧是原来的思路,将找“前面的比自己小且f最大的树”用线段树找出
利用的是本题的性质:先构成序列的一定比自己小,故以DP中的f为权值,用线段树维护最大值即可,可以随插入随处理,显然更优
\\(\\cal code:\\)
//线段树常规操作不写了
tr.build(1,1,n);
for(int i=1;i<=n;++i){
ans=tr.ask(1,1,pos[i])+1;
tr.change(1,pos[i],ans);
printf("%d\\n",tr.t[1].mx);
}
ICMP:Internet控制报文协议
是IP层的组成部分,传递差错报文或其他信息。
- 8位类型。表示该ICMP报文的含义。如目的不可达、超时、请求回显等。
- 8为代码。
进一步描写叙述该ICMP报文。ICMP报文的类型由类型字段和代码字段共同决定。
- 16位检验和。和IP首部检验和的算法同样。
回显请求和回显应答报文格式例如以下:
- 标识符。表示发送进程的ID号。
- 序号。
从0開始,每发送一个新的回显请求就加1.
- 选项数据。实际载荷,比如保存发送时间。接收端用当前时间减去发送时间就能计算出往返时间。
以上是关于20211007的主要内容,如果未能解决你的问题,请参考以下文章