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\\})\\)使下式最大化:(说法来自算进)

\\[\\frac{\\displaystyle \\sum_{i=1}^n a_i*x_i}{\\displaystyle \\sum_{i=1}^n b_i*x_i} \\]

做法是二分答案

对于二分的值mid,如果\\(mid\\leq ans\\)

\\[\\begin{aligned} &一定\\exists \\{x_1,\\cdots,x_n\\}使\\frac{\\displaystyle \\sum_{i=1}^n a_i*x_i}{\\displaystyle \\sum_{i=1}^n b_i*x_i}\\geq mid\\\\ &即\\displaystyle \\sum_{i=1}^n(a_i-mid* b_i)x_i\\geq 0 \\end{aligned} \\]

反之则左式一定为负,故可以用左式最大值是否非负作为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的战舰队列,支持两种操作:

  1. M i j 将第\\(i\\)号战舰所在队列整个接在第\\(j\\)号战舰所在队列后面

  2. 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\\)

两个形式有相似之处,实际上也是可以互相转化的,以下为推导过程:

\\[\\begin{aligned} 曼哈顿距离dis&=\\Delta x+\\Delta y\\\\ &=\\max(x1-x2,x2-x1)+\\max(y1-y2,y2-y1)\\\\ &=\\max(x_1-x_2+y_1-y_2,x_1-x_2+y_2-y_1,x_2-x_1+y_1-y_2,x_2-x_1+y_2-y_1)\\\\ &=\\max((x_1+y_1)-(x_2+y_2),(x_1-y_1)-(x_2-y_2),(x_2-y_2)-(x_1-y_1),(x_2+y_2)-(x_1+y_1))\\\\ &=\\max(|(x_1+y_1)-(x_2+y_2)|,|(x_1-y_1)-(x_2-y_2)|) \\end{aligned} \\]

如果令\\(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为例,我们化简一下式子

\\[\\begin{aligned} &\\displaystyle \\sum_{i=1}^{n}|x-x_i|(为了化简,对x进行排序)\\\\ =&-(x_1+\\cdots+x_i)+i*x_i+(x_{i+1}+\\cdots+x_n)-(n-i)*x_i(其中x_i是选中的聚会点)\\\\ =&-sumx_i+(sumx_n-sumx_i)+(2*i-n)x_i\\\\ =&(sumx_n-2*sumx_i)+(2*i-n)x_i \\end{aligned} \\]

于是就可以轻松的用前缀和搞定了,至于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\\)个人后少掉的最低高度(出去的人的最小高度和),转移式如下

\\[f_{i,j}=\\min \\begin{cases} &f_{i-1,j}\\\\ &f_{i-1,j-1}+h_i\\ \\ \\ \\ (i能出去) \\end{cases} \\]

蒟蒻习惯先写再压位,上式显然是个背包,压维方式是很常规的倒序枚举,不再赘述

最长上升子序列

题目描述

​ 给定一个序列,初始为空。现在我们将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)\\)

  1. 依序考虑每个数,建一个栈,如果这个数大于栈顶元素直接入栈,否则找到大于自己的第一个元素替换(二分),最后栈中元素个数为答案

这个方法虽然也还行,但需要不断重新扫整个数列,实测T7个点(加一个优化可以T3个点)

  1. 依旧是原来的思路,将找“前面的比自己小且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控制报文协议

ICMP:Internet控制报文协议。

 

是IP层的组成部分,传递差错报文或其他信息。

 
ICMP报文被封装在IP数据报内部:
技术分享
 
详细格式例如以下所看到的:
技术分享
 
个字段含义例如以下:
  • 8位类型。表示该ICMP报文的含义。如目的不可达、超时、请求回显等。
  • 8为代码。

     

    进一步描写叙述该ICMP报文。ICMP报文的类型由类型字段和代码字段共同决定

  • 16位检验和。和IP首部检验和的算法同样。
 
我们常常使用的ping程序就是基于ICMP报文进行的传输。pingclient发送一个ICMP回显请求报文,server收到此报文后返回一个ICMP回显应答报文作为应答。client和server都是在内核层发送和接受该报文的,而不是通过用户进程。

 

回显请求和回显应答报文格式例如以下:

技术分享
 
类型0 + 代码0 = 回显应答
类型8 + 代码0 = 回显请求
 
ICMP回显请求和回显应答报文多出了几个特有的字段:
  • 标识符。表示发送进程的ID号。
  • 序号。

     

    从0開始,每发送一个新的回显请求就加1.

  • 选项数据。实际载荷,比如保存发送时间。接收端用当前时间减去发送时间就能计算出往返时间。

     

     

以下是抓包的结果:
技术分享
 
client一共向server发送了4个回显请求。TTL字段是在IP首部中的。因为ICMP属于IP层协议,而IP层又是不可靠、无连接、尽力而为式的传输,所以ping偶尔会出现传输出错的情况。
 
參考:
《TCP/IP具体解释》第6章、第7章。

以上是关于20211007的主要内容,如果未能解决你的问题,请参考以下文章

微信小程序代码片段

VSCode自定义代码片段——CSS选择器

谷歌浏览器调试jsp 引入代码片段,如何调试代码片段中的js

片段和活动之间的核心区别是啥?哪些代码可以写成片段?

VSCode自定义代码片段——.vue文件的模板

VSCode自定义代码片段6——CSS选择器