2015.9.11模拟赛 codevs4162 bzoj1774【无双大王】

题目描述 Description

无双大王hzwer扫清六合,席卷八荒,万姓倾心,四方仰德。

hzwer拥有一片领土,其中有n个城市和m条双向道路。他规定每个人在领土上行走都要交过路费,同时进城也要交进城费。不同道路的过路费可能不同,不同城市的进城费可能不同。但是hzwer规定,如果缴纳x的进城费,那么所有小于x的进城费就不用缴纳了。(即只缴纳一条路径上的所有过路费和最大的进城费)那么从s城市出发到t城市,要缴纳多少费用?(s城市和t城市进城费也要算)

输入描述 Input Description

第一行两个数n,m,q。表示n个城市m条路q个询问。

接下来n个数,表示n个城市的进城费。

接下来m行,每行3个数,表示一条路径的两端和过路费。

接下来q行,每行两个数s,t,表示询问从s到t的最小花费。

输出描述 Output Description

对于每个询问,输出一行表示最小花费。

样例输入 Sample Input

5 7 2
2 5 3 3 4
1 2 3
1 3 2
2 5 3
5 3 1
5 4 1
2 4 3
3 4 4
1 4
2 3

样例输出 Sample Output

8

9

数据范围及提示 Data Size & Hint

对于20%数据,1<=n<=10.

对于60%数据,1<=n<=100.

对于100%数据,1<=n<=250,1<=m,q<=10000

题意是给定一个只有双向边的连通图,点有点权,边有边权,定义一条路径的费用是这条路径上的边权和加路径上的点权的最大值,询问两点之间的最小费用

看到n=250不难想到floyd.但是很容易发现,由于点权的影响,单纯地cost[i,k]+cost[k,j]到cost[i,j]的转移是不行的。

假设从点1到点2有两条路,一条dist=1,maxv=7.另一条dist=4,maxv=5.则第一条费用是8,第二条费用是9,我们会选择第1条。

但是假设从点2到点3有一条路,dist=3,maxv=4,容易看出一开始选第一条路费用是1+7+3=13,选第二条路费用是4+5+3=12.所以第2条会更优。

如果用个max[i,j]存i到j的最小费用路径上的点权最大值再想办法转移之类的也是不行的,因为不能转移的本质原因是不知道最大点权的点的位置,而求最短路又和路径有关。因此没办法判断最大点是否在某一段路径上,也就不能转移。除非再开一维保存最大点权的位置,才可以转移。那样复杂度就到n^4以上了。

回归到floyd的原型,其实就是个dp。f[i][j][k]表示从i到j的路径上除了两端点只经过编号1到k的点时的距离最小值。那么f[i][j][k]=min(f[i][j][k-1],f[i][k][k-1]+f[k][j][k-1]).最后一维因为总是从上一状态转移而来所以可以省略。

那么考虑floyd的枚举顺序,当我们枚举k,i,j时,经过的点只有i,j,还有编号1到k的k个点。如果按照点权从小到大排个序,那么编号为k的点比其他1到k中的点的点权都大。

所以只要点权考虑v[i],v[j],v[k]的最大值即可,而边权同floyd。

ps:"如果是一棵树的话,闲的没事还可以用link cut tree来做"——by hzwer

conclusion:一开始看到这道题的时候,我也没有从这方面去想。题目没有什么外延想法或者结论,只利用了floyd的性质,并从这一点出发,应该说整个思路都是很自然的。但是我们在背模板的时候,在为10分钟打出了无脑的线段树平衡树树套树而沾沾自喜的时候,是否注意到了这些算法背后的东西?如果没有积累一点深刻的认识,恐怕再简单不过的东西都能把你考倒。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdlib>
 5 #include<algorithm>
 6 #include<cmath>
 7 #include<queue>
 8 #include<deque>
 9 #include<set>
10 #include<map>
11 #include<ctime>
12 #define LL long long
13 #define inf 0x7fffffff
14 #define pa pair<int,int>
15 #define pi 3.1415926535897932384626433832795028841971
16 #define N 1010
17 using namespace std;
18 inline LL read()
19 {
20     LL x=0,f=1;char ch=getchar();
21     while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
22     while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
23     return x*f;
24 }
25 struct srt{int x,rnk;}cdk[1010];
26 bool operator <(srt a,srt b){return a.x<b.x;}
27 int dis[1010][1010];
28 int ans[1010][1010];
29 int v[1010];
30 int n,m,q;
31 int main()
32 {
33     memset(dis,127/3,sizeof(dis));
34     memset(ans,127/3,sizeof(ans));
35     for (int i=1;i<=n;i++)dis[i][i]=0;
36     n=read();m=read();q=read();
37     for (int i=1;i<=n;i++)
38     {
39         cdk[i].rnk=i;
40         cdk[i].x=read();
41         v[i]=cdk[i].x;
42     }
43     sort(cdk+1,cdk+n+1);
44     for (int i=1;i<=m;i++)
45     {
46         int x=read(),y=read(),z=read();
47         dis[x][y]=min(dis[x][y],z);
48         dis[y][x]=min(dis[y][x],z);
49     }
50     for (int kk=1;kk<=n;kk++)
51     {
52         int k=cdk[kk].rnk;
53         for (int i=1;i<=n;i++)
54         for (int j=1;j<=n;j++)
55             if (k!=i&&k!=j&&i!=j)
56             {
57                 dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
58                 ans[i][j]=min(ans[i][j],dis[i][j]+max(max(v[i],v[j]),v[k]));
59             }
60     }
61     for (int i=1;i<=q;i++)
62     {
63         int x=read(),y=read();
64         printf("%d\n",ans[x][y]);
65     }
66 }

codevs4162 && bzoj1774

时间: 2024-10-12 20:39:06

2015.9.11模拟赛 codevs4162 bzoj1774【无双大王】的相关文章

2015.9.11模拟赛 codevs 4159【hzwer的迷の数列】

题目描述 Description hzwer找了一个人畜无害的迷の数列…… 现在hzwer希望对这个数列进行一些操作,请你来回答hzwer的问题. 操作一:查询第i个数的大小 操作二:把第i个数的大小改成x 操作三:将整个序列反转.即把第i个数放到第n-i+1个. 输入描述 Input Description 输入数据第一行两个数n,m,表示数列长度和操作数. 第二行n个数,表示n个元素初始值. 以下m行,每行开头一个数opr,表示操作种类. opr=1,则后面接一个数i,表示查询第i个数大小.

2015.9.11模拟赛 codevs 4160【会玩的】

题目描述 Description hzwer真的很会玩啊…他有一个n*m的方格,每次可以给方格添加一整行或一整列,但是不能删除.现在他想要让总格子数超过k个,但是又想让总格子数尽可能小.请找出这时的n,m.如果有多解,输出任意一种方案. 输入描述 Input Description 一行3个数n,m,k. 输出描述 Output Description 第一行一个数ans,表示此时的方格数. 第二行两个数m’,n’,表示此时的行数列数.如果有多解,输出任意一种方案 样例输入 Sample Inp

2017 6 11模拟赛

盘子序列 [题目描述] 有 n 个盘子.盘子被生产出来后,被按照某种顺序摞在一起.初始盘堆中如果一 个盘子比所有它上面的盘子都大,那么它是安全的,否则它是危险的.称初始盘堆为 A,另外有一个开始为空的盘堆 B.为了掩盖失误,生产商会对盘子序列做一些“处 理” ,每次进行以下操作中的一个:(1)将 A 最上面的盘子放到 B 最上面:(2)将 B 最上 面的盘子给你. 在得到所有n个盘子之后, 你需要判断初始盘堆里是否有危险的盘子. [输入格式] 输入文件包含多组数据(不超过 10 组) 每组数据的

ZROI 19.08.11模拟赛

传送门 写在前面:为了保护正睿题目版权,这里不放题面,只写题解. dlstql,wsl A \(10pts:\) \(a=100,T=100\),对每个排列构造一个反的,一步到位即可. \(20pts:\) \(a=50\),构造\(1\)和所有元素交换的排列,实现交换\((v,u)\)可以令两者分别与\(1\)交换,选择排序即可. \(40pts:\) \(a=30\),构造前\(25\)个元素与\(1\)交换的排列,另有一个排列交换前\(25\)个与后\(25\)个元素. \(a=20\)时

2017.6.11 校内模拟赛

题面及数据及std(有本人的也有原来的) :2017.6.11 校内模拟赛 T1 自己在纸上模拟一下后就会发现 可以用栈来搞一搞事情 受了上次zsq 讲的双栈排序的启发.. 具体就是将原盘子大小copy一下排个序 用两个指针维护两个数组(原数据 和 排序后的数据), 即分为1数据和2数组 将小于1指针指向的数据的2数组中的数据全部压入栈中 后进行消除, 将栈栈顶元素与当前1数组中的1指针指向的元素进行比较 相同则消除 后重复过程 直至指针超过N 后判断一下是否两个指针都超过了N... #incl

2017.11.25【NOIP提高组】模拟赛A组

2017.11.25[NOIP提高组]模拟赛A组 T1 3467. [NOIP2013模拟联考7]最长上升子序列(lis) T2 3468. [NOIP2013模拟联考7]OSU!(osu) T3 3472. [NOIP2013模拟联考8]匹配(match) T1 有转移方程f[i]=max{f[j]}+1,a[j]<a[i] 可以用线段树+离散化维护这个方程,因为涉及以往状态可以用主席树维护 打太丑爆空间了 Code 1 #include<cstdio> 2 #include<c

11.27 模拟赛

并没有人做的模拟赛... 出题人hx,,, T1:就是上一道矩阵乘法 数学题 T2: 一个数列中 一个区间满足,存在一个k(L <= k <= R),并且对于任意的i (L <= i <= R),ai都能被ak整除 这样的一个特殊区间 [L, R]价值为R - L 想知道序列中所有特殊区间的最大价值是多少,而有多少个这样的区间呢 这些区间又分别是哪些呢 输出每个区间的L 思路: 用两个ST表分别求一段区间的gcd和最小值 然后可以二分答案 check的时候枚举左端点,判断在这段区间

2017 9 11 noip模拟赛T2

#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=205; int map[N][N]; int d[N],tag[N],book[N],f[N]; int n,m; void work(int x) { memset(d,63,sizeof(d)); memset(book,0,sizeof(book)); memset(f,0,sizeof(

【2018.11.22】ctsc2018(模拟赛!)

太蠢了……$noip$ 后第一次模拟赛竟然是这样的……完全就是打击自信 / 降智…… 1. 假面 一道神仙概率 $dp$!第一次写…… 拿到题就发现血量 $m_i$ 的上限只有 $100$! 然后 $0$ 操作就可以用 $rate(i,j)$ 动态维护第 $i$ 个人血量为 $j$ 的概率啦. $1$ 操作比较麻烦(但是它故意弄得很少). 设 $live_i$ 和 $dead_i$ 分别为 $1$ 操作范围内的第 $i$ 个人活着和死了的概率,$g_{i,j}$ 是除 $i$ 以外有 $j$ 个