1009题目链接

设 fijkf_{ijk}fijk​ 表示从 (1,1) 走到 (i,j),一路上收集了 k 个钻石时,钻石的单价最高能涨到多少,则 ans = max(k × f n,n,k )。
对于固定的 (i,j) 来说,考虑两个状态fijxf_{ijx}fijx​和 fijyf_{ijy}fijy​,其中 x < y,如果fijxf_{ijx}fijx​ ≤ fijxf_{ijx}fijx​,则状态 fijxf_{ijx}fijx​ 一定不可能发展为最优解,可以剔除。对于每个 (i,j),用列表按照 k 升序保存所有状态,并剔除不可能成为最优解的状态即可。
随机数据下当 n = 100 时,单个 (i,j) 的有效状态的峰值 k 大约为几千。时间复杂度O(n 2 k)。

完整代码:
#define _CRT_secondCURE_NO_WARNINGS
#include<cstdio>
#include<iostream>
#include<vector>
#include<string>
#include<queue>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> P;
typedef vector<P> V;int n;
ll a[105][105], b[105][105];
vector<P> p[105][105];void solve(const V& x, const V& y, V& z)
{int sx = x.size();int sy = y.size();int i = 0, j = 0;while ((i < sx || j < sy) && z.size() < 100){if (i < sx && j < sy) z.push_back(x[i].first * x[i].second > y[j].first * y[j].second ? x[i++] : y[j++]);else if (i < sx) z.push_back(x[i++]);else if (j < sy) z.push_back(y[j++]);}
}bool cmp(P x, P y)
{return x.first * x.second > y.first * y.second;
}int main()
{int T = 1;scanf("%d", &T);while (T--){scanf("%d", &n);for (int i = 1; i <= n; i++)for (int j = 1; j <= n; j++)scanf("%lld", &a[i][j]), p[i][j].clear();for (int i = 1; i <= n; i++)for (int j = 1; j <= n; j++)scanf("%lld", &b[i][j]);p[1][1].push_back(make_pair(a[1][1], b[1][1]));for (int i = 1; i <= n; i++)for (int j = 1; j <= n; j++){if (i == 1 && j == 1) continue;  //当前在(1,1),直接跳过if (i == 1) p[i][j].push_back(p[i][j - 1][0]);  //当前在第一行,则直接初始化为左边一列else if (j == 1) p[i][j].push_back(p[i - 1][j][0]); //当前在第一列,用上一行初始化else solve(p[i - 1][j], p[i][j - 1], p[i][j]);  //其他情况对坐标(i-1,j)和(i,j-1)选取最大值,将比较结果存入p[i][j];for (int k = 0; k < p[i][j].size(); k++) p[i][j][k].first += a[i][j], p[i][j][k].second += b[i][j];    //对当前每一个位置下的每一种状态加上当前单元格的收益sort(p[i][j].begin(), p[i][j].end(), cmp);   //对每一个位置的每一种状态的收益进行排序}ll ans = 0;for (int i = 0; i < p[n][n].size(); i++){ans = max(ans, p[n][n][i].first * p[n][n][i].second);  //对(n,n)位置的每一种可能状态进行比较,取最大。}printf("%lld\n", ans);}return 0;
}

1010题目链接

分析:
将原始边作为白边,折扣边作为黑边,由于同一条边不可能选择两次,那么问题等价于求包含恰好 k 条黑边的最小生成树。这是一个经典问题,令 f(k) 表示包含恰好 k 条黑边的最小生成树的边权和,则 f(k) 是一个凸函数,求出 f(k) 的方法为:
• 选择参数 c,将每条黑边的边权都加上 c。
• 求出修改边权后的图的最小生成树,令 sum( c ) 为对应的边权和,l( c ) 为最小生成树中使用黑边数量的最小值,r( c ) 为最小生成树中使用黑边数量的最大值。
• 二分找到合适的参数 c,满足 l( c ) ≤ k ≤ r( c ),则 f(k) = sum( c ) − k × c。
由于边权在 [1,1000] 之间,因此可以预处理出 c = −1000…1000 的所有信息,一共需要求 O( c ) 次最小生成树。注意到如果对黑边或者白边单独求最小生成树,则非树边不可能用到,因此可以将边数缩减至 O(n)。总时间复杂度 O(mlogn + nclogn)。
这里给出最小生成树模板(kruskal)

#define _CRT_SECURE_NO_WARNINGS   //模板题:洛谷P3366
#include<cstdio>
#include<iostream>
#include<vector>
#include<string>
#include<queue>
#include<algorithm>
#define maxn 5005
using namespace std;
int fa[maxn];
typedef struct node
{int from;int to;int w;
}node;
vector<node> edge(200005);
bool cmp(node i, node j)
{return i.w < j.w;
}
void init(int n)
{for (int i = 0; i < n; ++i)fa[i] = i;
}
int find(int x)
{return x == fa[x] ? x : find(fa[x]);
}
void merge(int i, int j)
{fa[find(j)] = find(i);
}
int main()
{int n, m,k=0,total=0;scanf("%d%d", &n, &m);for (int i = 0; i < m; ++i)2scanf("%d%d%d", &edge[i].from, &edge[i].to, &edge[i].w);init(n);sort(edge.begin(), edge.begin()+m, cmp);for (int i = 0; i <= m; ++i){if (k == n - 1) break;if (find(edge[i].from) != find(edge[i].to)){merge(edge[i].from, edge[i].to);total += edge[i].w;k++;}}int count = 0;for (int i = 1; i <= n; ++i){if (fa[i] == i)count++;}if (count > 1)cout << "orz" << endl;elsecout << total << endl;return 0;
}

prim模板:

#define _CRT_SECURE_NO_WARNINGS
#include<cstdio>
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
#define INFINITE 0x3FFFFFFF
#define VertexData unsigned int  //顶点数据
#define vexCounts 5005  //顶点数量
int n;
struct node
{VertexData data;unsigned int lowestcost;
}closedge[vexCounts]; //Prim算法中的辅助信息
int Minmum(struct node* closedge)  //返回最小代价边
{unsigned int min = INFINITE;int index = 0;for (int i = 0; i < vexCounts; i++){if (closedge[i].lowestcost < min && closedge[i].lowestcost != 0){min = closedge[i].lowestcost;index = i;}}return index;
}
int MiniSpanTree_Prim(vector<vector<int> > &adjMat, VertexData s)
{int sum = 0;for (int i = 1; i <=n; i++){closedge[i].lowestcost = INFINITE;}closedge[s].data = s;      //从顶点s开始closedge[s].lowestcost = 0;for (int i = 1; i <=n; i++)  //初始化辅助数组{if (i != s){closedge[i].data = s;closedge[i].lowestcost = adjMat[s][i];}}for (int e = 1; e <n; e++)  //n-1条边时退出{int k = Minmum(closedge);  //选择最小代价边sum += closedge[k].lowestcost;closedge[k].lowestcost = 0; //代价置为0for (int i = 2; i <=n; i++)  //更新v中顶点最小代价边信息{if (adjMat[k][i] < closedge[i].lowestcost){closedge[i].data = k;closedge[i].lowestcost = adjMat[k][i];}}}return sum;
}
int main()
{vector<vector<int> >adjMat(vexCounts, vector<int>(vexCounts, INFINITE));int m;cin >> n >> m;for (int i = 0; i < m; ++i){int x, y, z;scanf("%d%d%d", &x, &y, &z);adjMat[x][y] = z;adjMat[y][x] = z;}int ans = MiniSpanTree_Prim(adjMat, 1); //Prim算法,从顶点1开始.if (abs(ans) >= INFINITE)cout << "orz" << endl;elsecout << ans << endl;return 0;
}
此题AC代码:
#include<cstdio>
#include<algorithm>
using namespace std;
typedef pair<int,int>P;
const int N=1005,M=200005,V=1000;
int Case,n,m,i,f[N];P fl[V*2+5];
struct E{int x,y,w;}a[M],b[M];
inline bool cmp(const E&a,const E&b){return a.w<b.w;}
int F(int x){return f[x]==x?x:f[x]=F(f[x]);}
inline bool merge(int x,int y){if(F(x)==F(y))return 0;f[f[x]]=f[y];return 1;
}
inline void reduce(E*e){sort(e+1,e+m+1,cmp);for(int i=1;i<=n;i++)f[i]=i;for(int i=1,cnt=0;i<=m;i++)if(merge(e[i].x,e[i].y))e[++cnt]=e[i];
}
inline P call(int k){for(int i=1;i<=n;i++)f[i]=i;int A=1,B=1,sum=0,cnt=0;while(A<n&&B<n){if(a[A].w<=b[B].w+k){if(merge(a[A].x,a[A].y))sum+=a[A].w;A++;}else{if(merge(b[B].x,b[B].y))sum+=b[B].w+k,cnt++;B++;}}while(A<n){if(merge(a[A].x,a[A].y))sum+=a[A].w;A++;}while(B<n){if(merge(b[B].x,b[B].y))sum+=b[B].w+k,cnt++;B++;}return P(sum,cnt);
}
inline int ask(int k){for(int i=-V;i<=V;i++)if(fl[i+V].second<=k)return fl[i+V].first-k*i;return -1;
}
int main(){scanf("%d",&Case);while(Case--){scanf("%d%d",&n,&m);for(i=1;i<=m;i++){scanf("%d%d%d%d",&a[i].x,&a[i].y,&a[i].w,&b[i].w);b[i].x=a[i].x,b[i].y=a[i].y;}reduce(a);reduce(b);for(i=-V;i<=V;i++)fl[i+V]=call(i);for(i=0;i<n;i++)printf("%d\n",ask(i));}
}

2021杭电多校补题(3)相关推荐

  1. 2021杭电多校补题——第一场

    2021杭电多校补题--第一场 文章目录 Mod, Or and Everything Rocket land(待补) Puzzle loop(待补) Another thief in a Shop( ...

  2. 2021杭电多校补题(9)

    1. Integers Have Friends 2.0 传送门 题意: 给出一个长度为nnn 的数组,选出一个最长的子序列,存在一个 m≥2m \geq 2m≥2 使得 ak1%m=ak2%m=ak ...

  3. 2021杭电多校补题(6)

    1001题目链接 题意:给定数x,求包含x的区间和为素数的最小区间长度 分析: 显然 r > 0,r ≥ l,如果 l ≤ 0,此时 [l,r] 区间和 = [−l + 1,r] 的区间和,且 ...

  4. 2021杭电多校补题(4)

    1008题目链接 题意:给定一张n*m的方格,起点在左上角,只能向下或向右走,地图中有k个地雷,人不能走到地雷上,问可能到达的点有多少. 我们考虑行的转移,在这一行上初始不可达点是输入中的点,对于这样 ...

  5. 2021杭电多校补题(1)

    1005 https://acm.hdu.edu.cn/showproblem.php?pid=6954) 思路:边权为lcm(a,b).因为lcm(a,b)=a*b/gcm(a,b),所以有两种情况 ...

  6. 2021杭电多校补题(5)

    传送门 1. Array 题意: 给出一个长度为 nnn 的数组,求其有多少个区间满足其众数个数大于区间长度的一半. 题解: 考虑分治. 对于一个区间(l,r)(l,r)(l,r),其中点为 midm ...

  7. 2021杭电多校补题(2)

    1. I love max and multiply 题意: 给出两个数组aaa和bbb ,定义ccc数组 ck=max(ai⋅bj)c_k =max(a_i \cdot b_j)ck​=max(ai ...

  8. 2021杭电多校第八场补题

    比赛传送门:Contest Problem List (hdu.edu.cn) 1006)GCD Game 题目翻译:爱丽丝和鲍勃正在玩游戏. 他们轮流操作.有n个数字,a1,a2,...,an.每次 ...

  9. 【2021杭电多校赛】2021“MINIEYE杯”中国大学生算法设计超级联赛(10)签到题2题

    Solved Pro.ID Title Ratio(Accepted / Submitted) 1001 Pty loves sequence 25.00%(52/208) 1002 Pty with ...

最新文章

  1. 程序员版本管理知识 Git 详细整理
  2. Html5 Game - SpaceWar
  3. 懒办法1篇文10分钟快速入门MySQL增删查改
  4. 中国第一批写java的人_中国java开源界最可爱的人们
  5. Mysql 无法插入中文,中文乱码解决
  6. 拓扑排序 详解 + 并查集 详解 + 最小生成树详解
  7. 数据结构学习笔记(六)链表算法题
  8. bzoj1935 [Shoi2007]Tree 园丁的烦恼 二维偏序
  9. phpstorm 的下载、安装与激活
  10. bzoj3262: 陌上花开 树套树
  11. 移动硬盘只显示盘符,不显示容量,也不能打开
  12. 【7】OPencv骨架细化算法
  13. signature=506ccff074d130c2e8d4e3268d3b44f1,Secure proxy signature schemes from the Weil pairing
  14. 【计算机网络】,java基础教程从入门到精通
  15. 基于ndis的防火墙源码供大家赏析firewall c
  16. VM Workstation 16 Pro 下载安装以及下载配置Linux虚拟机(操作如下)
  17. 如何开机进入grub界面_开机显示grub怎么办,开机进入grub界面
  18. 第四届河南省程序设计大赛D nyoj306-走迷宫【二分DFS、最短路Dijkstra】
  19. Edge浏览器访问特殊网站端口(如10080)出现ERR_UNSAFE_PORT解决办法
  20. 【跟着ChatGPT学深度学习】ChatGPT带我入门深度学习

热门文章

  1. 在培训机构学习java后端的感想
  2. 有没有好用的资产管理系统推荐(免费好用的it资产管理系统)
  3. B+树,B树,聚集索引,非聚集索引
  4. CentOS7 启动时出现:Redirecting to /bin/systemctl start httpd.service
  5. 可以让你在mac上学习圣经的软件:Bible for Mac
  6. 如何在谷歌浏览器上,查看手机客户端页面
  7. vba批量合并指定的sheet_Excel通用VBA代码一键合并多个工作表至总表!
  8. 【科技百咖】曾经的网络安全少帅,今天的移动拓客达人
  9. SAP PA培训总结
  10. EMNLP2022 信息抽取和实体识别相关论文汇总