2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解

😊 | Powered By HeartFireY | MINIEYE Contest 1 Solution

D.Game on Plane

Problem Description

Alice and Bob are playing a game. In this game, there are 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_i++ straight lines on the 2D plane. Alice will select exactly 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_02 straight lines 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_图层_03 among all the 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_i++ straight lines first, then Bob will draw a straight line 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_05. The penalty of Bob is defined as the number of lines in 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_06 that shares at least one common point with 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_05. Note that two overlapping lines also share common points.

Alice wants to maximize the penalty of Bob while Bob wants to minimize it. You will be given these 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_i++ lines, please write a program to predict the penalty of Bob for 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_算法_09 if both of the players play optimally.

Input

The first line contains a single integer 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_10 (2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_11), the number of test cases. For each test case:

The first line contains a single integer 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_i++ (2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_acm竞赛_13), denoting the number of straight lines.

Each of the next 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_i++ lines contains four integers 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_算法_15 and 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_算法_16 (2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_17, denoting a straight line passes both 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_acm竞赛_18 and 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_acm竞赛_19. 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_acm竞赛_18 will never be coincided with 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_acm竞赛_19.

It is guaranteed that the sum of all 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_i++ is at most 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_图层_23​​​.

Output

For each test case, output 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_i++ lines, the 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_acm竞赛_25-th (2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_算法_26) of which containing an integer, denoting the penalty of Bob when 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_27.

Solution

😀 算法标签:计算几何

要获得尽可能多的交点数,只需要保证穿过尽可能多的斜率不同的直线即可;

要获取尽可能少的交点数,只需要保证找到尽可能多的斜率相同的直线即可。

Alice 为了让 Bob 与尽量多的直线相交,最优策略就是最小化斜率出现次数的最大值,所以不断从每种斜率的直线中各选一种即可。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e6 + 10;
map<double, int> mp;
int f[maxn];
pair<double, int> pus;

signed main(){
ios_base::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int t = 0; cin >> t;
while(t--){
int n = 0, nows = 0, maxx = INT_MIN, cnt = 0; cin >> n;
mp.clear();
for(int i = 0; i < n; i++){
int nx, ny, mx, my; cin >> nx >> ny >> mx >> my;
mx == nx ? (nows++) : (mp[(my - ny) * 1.0 / (mx - nx)]++);
}
memset(f, 0, (n + 10) * sizeof(int));
for(map<double,int>::iterator i = mp.begin(); i != mp.end(); i++)
pus=*i, f[pus.second]++, maxx=max(maxx, pus.second);
f[nows]++, maxx = max(maxx, nows);
for(int i = maxx; i; i--) f[i] += f[i + 1];
for(int i = 1;i <= maxx; i++){
while(f[i]--) cnt++,printf("%d\n",cnt - i);
}
}

return 0;
}

G.Photoshop Layers

Solution

😀 算法标签:前缀和

队友写的,简述一下思路:

首先每个图层有两种情况

  1. 图层置为当前颜色(“覆盖”)
  2. 图层置为上一层颜色+当前层颜色(“重叠”)

首先按输入顺序进行扫描,处理出对于每个图层2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_acm竞赛_25​​而言左侧第一个"覆盖"的图层,用2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_29表示。然后预处理出前缀和。

则对于每个询问,根据2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_29值找到2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_图层_31,剩余的中间部分直接利用前缀和相减求出答案即可。

放个标程。。。

#include <cstdio>
const int N = 100005;
int Case, n, m, i, x, y, t[N], r[N], g[N], b[N], f[N];
inline int ask(int *s, int l, int r)
{
int x = f[r], ret;
if (x < l) ret = s[r] - s[l - 1];
else ret = s[r] - s[x - 1];
return ret < 255 ? ret : 255;
}
int main()
{
scanf("%d", &Case);
while (Case--)
{
scanf("%d%d", &n, &m);
for (i = 1; i <= n; i++)
{
scanf("%d%X", &t[i], &x);
b[i] = x & 255;
x >>= 8;
g[i] = x & 255;
x >>= 8;
r[i] = x;
r[i] += r[i - 1];
g[i] += g[i - 1];
b[i] += b[i - 1];
if (t[i] == 1) f[i] = i;
else f[i] = f[i - 1];
}
while (m--)
{
scanf("%d%d", &x, &y);
printf("%02X%02X%02X\n", ask(r, x, y), ask(g, x, y), ask(b, x, y));
}
}
}

I.Rise in Price

Problem Description

There are 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_acm竞赛_32 cells on a grid, the top-left cell is at 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_33 while the bottom-right cell is at 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_图层_34. You start at 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_33 and move to 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_图层_34. At any cell 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_算法_37, you can move to 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_i++_38 or 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_39, provided that you don’t move out of the grid. Clearly, you will make exactly 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_acm竞赛_40

When you are at cell 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_算法_37, including the starting point 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_33 and the destination 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_图层_34, you can take all the 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_acm竞赛_44 diamonds at this cell, and have a chance to raise the price of each diamond by 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_图层_45 dollars. You will sell all the diamonds you have with the final price in the end, and your goal is to choose the optimal path that will maximize your profits. Note that initially the price of each diamond is zero, and you have nothing to sell.

Input

The first line contains a single integer 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_10 (2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_acm竞赛_47), the number of test cases. For each test case:

The first line contains a single integer 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_i++ (2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_49), denoting the size of the grid.

Each of the following 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_i++ lines contains 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_i++ integers, the 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_acm竞赛_25-th line contains 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_acm竞赛_53 (2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_i++_54), denoting the number of diamonds in each cell.

Each of the following 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_i++ lines contains 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_i++ integers, the 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_acm竞赛_25-th line contains 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_算法_58 (2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_acm竞赛_59), denoting how much you can raise the price in each cell.

It is guaranteed that all the values of 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_acm竞赛_44 and 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_图层_45 are chosen uniformly at random from integers in 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_acm竞赛_62​​​​​. The randomness condition does not apply to the sample test case, but your solution must pass the sample as well.

Output

For each test case, output a single line containing an integer: the maximum number of dollars you can earn by selling diamonds.

Solution

😀 算法标签:启发式搜索

带点权双约图束寻路,单图范围100*100,搜索只有两个方向,考虑启发式搜索:首先构造估值函数的计算方式

计算每个点对于答案的贡献:对于当前点而言,起点到当前点路径上的点权和(已走路径)记为2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_63​,当前点到终点(未走路径)的路径点权和为2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_图层_64(假设路径已经确定),那么当前点的贡献可以表示为2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_acm竞赛_65

但是在实际的路线中,对于已走路径是唯一确定的,而未走路径是不确定的,因此我们可以假设某条可被选的路径所有权值之和为2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_acm竞赛_66​​​​,对于2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_67​​​​的最大值(即2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_68​​最大值​)我们可以由2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_69​​​向2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_算法_70​​​进行状态转移(转移时取2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_算法_71​​​​)得到,是一个已经确定的值,状态转移方程如下:
2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_i++_72

我们可以对贡献方程进行变形:2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_图层_73​​,展开可得2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_acm竞赛_74​​,其中变量为2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_算法_75​​,对2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_算法_75​​求导或直接由二次方程对称轴可得当贡献取极大值时,2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_77​​,则2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_算法_78​​。代入贡献方程:
2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_acm竞赛_79
那么对于每个点可以计算其贡献值,如果贡献值小于已知答案,那么终止搜索;如果贡献值大于已知答案,则继续搜索。

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int maxn = 110;

int n = 0, ans = 0, maxval[maxn][maxn], a[maxn][maxn], b[maxn][maxn];

inline int h(int x, int y, int sum1, int sum2){ return ((sum1 + sum2 + maxval[x][y]) >> 1); }

void dfs(int x, int y, int cnta, int cntb){
if(x == n && y == n){
ans = max(ans, cnta * cntb);
return;
}

int h0 = h(x + 1, y, cnta, cntb), h1 = h(x, y + 1, cnta, cntb);
if(x < n && h0 * h0 > ans) dfs(x + 1, y, cnta + a[x + 1][y], cntb + b[x + 1][y]);
if(y < n && h1 * h1 > ans) dfs(x, y + 1, cnta + a[x][y + 1], cntb + b[x][y + 1]);
}

signed main(){
ios_base::sync_with_stdio(false);
int t = 0; cin >> t;
while(t--){
cin >> n;
ans = 0;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++) cin >> a[i][j];
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++) cin >> b[i][j];
for(int i = n; i > 0; i--)
for(int j = n; j > 0; j--){
maxval[i][j] = a[i][j] + b[i][j];
if(i != n)
maxval[i][j] = max(maxval[i][j], a[i][j] + b[i][j] + maxval[i + 1][j]);
if(j != n)
maxval[i][j] = max(maxval[i][j], a[i][j] + b[i][j] + maxval[i][j + 1]);
}
dfs(1, 1, a[1][1], b[1][1]);
cout << ans << endl;
}
return 0;
}

K.Segment Tree with Pruning

Problem Description

Chenjb is struggling with data stucture now. He is trying to solve a problem using segment tree. Chenjb is a freshman in programming contest, and he wrote down the following C/C++ code and ran ‘’2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_80’’ to build a standard segment tree on range 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_81:

Node* build(long long l, long long r) {
Node* x = new(Node);
if (l == r) return x;
long long mid = (l + r) / 2;
x -> lchild = build(l, mid);
x -> rchild = build(mid + 1, r);
return x;
}

Chenjb submitted his code, but unfortunately, got MLE (Memory Limit Exceeded). Soon Chenjb realized that his program will new a large quantity of nodes, and he decided to reduce the number of nodes by pruning:

Node* build(long long l, long long r) {
Node* x = new(Node);
if (r - l + 1 <= k) return x;
long long mid = (l + r) / 2;
x -> lchild = build(l, mid);
x -> rchild = build(mid + 1, r);
return x;
}

You know, Chenjb is a freshman, so he will try different values of 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_02 to find the optimal one. You will be given the values of 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_i++ and 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_02​​, please tell him the number of nodes that will be generated by his new program.

Input

The first line contains a single integer 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_10 (2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_86), the number of test cases. For each test case:

The only line contains two integers 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_i++ and 2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_02 (2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_i++_89), denoting a query.

Output

For each query, print a single line containing an integer, denoting the number of segment tree nodes.

Solution

😀 算法标签:记忆化搜索

回想线段树建树过程,容易知道:对于一定长度的区间2021“MINIEYE杯”中国大学生算法设计超级联赛(3)题解_斜率_90,线段树所需要的点数是一定的,那么我我们没有必要对所有的区间进行递归计数,而只需要在搜索过程中记录已有长度对应的点数,然后再次遇到该区间长度时直接返回上次记录的值即可。

记忆化方式打map标记即可,用数组不方便。

#include <bits/stdc++.h>
#define int long long
using namespace std;

map<int, int> mp;
int n = 0, k = 0;

inline void init(){ mp.clear(); }

int dfs(int l, int r){
if(r - l + 1 <= k) return 1;
if(mp.find(r - l + 1) != mp.end()) return mp[r - l + 1];
int mid = l + r >> 1, now = 1;
now += dfs(l, mid), now += dfs(mid + 1, r);
mp[r - l + 1] = now;
return now;
}


signed main(){
ios_base::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int t = 0; cin >> t;
while(t--){
cin >> n >> k;
init();
cout << dfs(1, n) << endl;
}
return 0;
}