题目连接:
PTA | 程序设计类实验辅助教学平台千名教师建设,万道高质量题目,百万用户拼题的程序设计实验辅助教学平台
https://pintia.cn/problem-sets/1459829212832296960/problems/1459829264400629764
题目大意:
n个点均分一个圆,编号是1-n,给m条边,若由i,j和p,q组成的两条线相交,交点的贡献是(i + j ) * (p * q),删去两个点,问剩下的交点权值最大为多少。(N < =1000, M <= 10000)
解题思路:
我们可以很容易地想到,权值最大为总的权值和-第一个点贡献的权值和-第二个点贡献的权值和+第一个点和第二个点共同作用贡献的权值和。
线(i,j)将圆分为两个圆弧,要使与其有交点,对于线(s,t)需要满足s∈(i,j),t∈(j,i)即两段圆弧。那么对于线(i,j)的贡献 = * (i + j)
对于前者,其实是邻接矩阵上的一个子矩阵,可以用二维前缀和求出。
由于在统计时,一个点被两条边算入贡献,因此总的权值和要除以二。
我们再枚举点对,删除点i时,点j的贡献是多少,再取max。
具体实现看代码
代码实现:
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N = 1e3 + 100;
const int M = 1e5 + 100;
int n, m, g[N][N];
ll sum[N], du[N];//sum数组表示,删去点i的贡献值
struct node {
int x, y;
} line[M];
int cal(int xa, int ya, int xb, int yb) { //计算邻接矩阵中的子矩阵
if (xa > xb || ya > yb) return 0;
return g[xb][yb] - g[xa - 1][yb] - g[xb][ya - 1] + g[xa - 1][ya - 1];
}
int ask(int x, int y) { //计算与(x,y)有交点的线的贡献。
if (cal(x, y, x, y) == 0) return 0;
if (x > y) swap(x, y);
return cal(x + 1, y + 1, y - 1, n) + cal(x + 1, 1, y - 1, x - 1);
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
int x, y;
scanf("%d%d", &x, &y);
if (x > y) swap(x, y);
line[i].x = x;
line[i].y = y;
g[x][y] += x + y;
g[y][x] += x + y;
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
g[i][j] += g[i - 1][j] + g[i][j - 1] - g[i - 1][j - 1];
}
}
ll num = 0;
for (int i = 1; i <= m; i++) {
num += 1ll * (line[i].x + line[i].y) * ask(line[i].x, line[i].y);
}
num /= 2;
ll ans = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (i == line[j].x || i == line[j].y) continue;
if (line[j].x + 1 == line[j].y) continue;
if (i > line[j].x && i < line[j].y) {
sum[i] += 1ll * (line[j].x + line[j].y) * (cal(i, 1, i, line[j].x - 1) + cal(i, line[j].y + 1, i, n));
} else {
sum[i] += 1ll * (line[j].x + line[j].y) * cal(i, line[j].x + 1, i, line[j].y - 1);
}
}
}
for (int i = 1; i <= n; i++) {
memset(du, 0, sizeof(du));
for (int j = 1; j <= m; j++) {
if (i == line[j].x || i == line[j].y) continue;
if (line[j].x + 1 == line[j].y) continue;
ll t = 0;
if (i > line[j].x && i < line[j].y) {
t = 1ll * (line[j].x + line[j].y) * (cal(i, 1, i, line[j].x - 1) + cal(i, line[j].y + 1, i, n));
} else {
t = 1ll * (line[j].x + line[j].y) * cal(i, line[j].x + 1, i, line[j].y - 1);
}
du[line[j].x] += t;
du[line[j].y] += t;
}
for (int j = 1; j <= n; j++) {
if (i == j) continue;
ans = max(ans, num - (sum[i] + sum[j] - du[j] - (i + j) * ask(i, j)));
// num 总贡献
//(sum[i] + sum[j]) 点i和j独立作用的权值和
// du[j] + (i + j) * ask(i,j) 点i和j共同作用的权值和
}
}
printf("%lld\n", ans);
return 0;
}