省选测试39
A 老洪的遍历
题目大意 : 从一个点走到另一个点会有一定贡献,问按要求走完一些点再回到原点能拿到的最大收益
-
给了a[i],s是a的前缀和,从i点到j点收益是 \(\frac{(a_i-a_j)s_is_j}{2a_ia_j}\) ,拆一下就是 \(\frac{1}{2}(s_i\frac{s_j}{a_j}-s_j\frac{s_i}{a_i})\)
-
发现如果设第i个点的向量是 \((s_i,\frac{s_i}{a_i})\) , 那就是两个点的向量点乘起来除以2,就是和原点组成三角形的面积
-
由于要回到原点,最后会形成一个封闭图形,最后让面积最大,然后就是维护凸包,计算面积即可
Code
Show Code
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e5 + 5;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar()) x = x * 10 + c - '0';
return x * f;
}
struct Node {
double x, y;
}a[N], stk[N];
Node operator - (const Node &a, const Node &b) {
return (Node) {a.x - b.x, a.y - b.y};
}
double operator * (const Node &a, const Node &b) {
return a.x * b.y - a.y * b.x;
}
bool operator < (const Node &a, const Node &b) {
return a.x != b.x ? a.x < b.x : a.y < b.y;
}
int n, s, tp, k;
double ans;
int main() {
freopen("visit.in", "r", stdin);
freopen("visit.out", "w", stdout);
n = read();
for (int i = 1; i <= n; ++i) {
int x = read(); s += x;
a[i] = (Node) {1.0 * s, 1.0 * s / x};
}
sort(a + 1, a + n + 1);
stk[k = ++tp] = a[1];
for (int i = 2; i <= n; ++i) {
while (tp > k && (stk[tp] - stk[tp-1]) * (a[i] - stk[tp-1]) >= 0) tp--;
stk[++tp] = a[i];
}
k = tp;
for (int i = n - 1; i >= 1; --i) {
while (tp > k && (stk[tp] - stk[tp-1]) * (a[i] - stk[tp-1]) >= 0) tp--;
stk[++tp] = a[i];
}
for (int i = 1; i < tp; ++i)
ans += stk[i] * stk[i+1];
printf("%.5lf\n", -ans / 2);
return 0;
}
B 老洪的神秘操作
题目大意 : 给一个序列,每次可以区间加一个数,问让每个数在模7的意义下与0同余所需的最少操作次数
-
看到区间加可以转换成差分,最后就是让差分数组为零,稍微想想就会发现,最后求的其实是把n个数分组,让和为0的组数尽量多
-
0的肯定自己一组,1+6,2+5,3+4都是可以直接匹配起来的,所以最后最多剩下3个数
-
设f[i][j][k][l]为第一个数选了i个,第二个数选了j个,第三个数选了k个,最后一组和为l,可以分出的最多组数
-
转移的时候枚举三个数放的是哪个。空间开不下,第一维滚动压掉就行
Code
Show Code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 501;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; (c < '0' || c > '9'); c = getchar()) if (c == '-') f = -1;
for (;!(c < '0' || c > '9'); c = getchar()) x = x * 10 + c - '0';
return x * f;
}
int n, s[9], ans, f[N][N][9], g[N][N][9], a[9], cnt, mx;
int main() {
freopen("secret.in", "r", stdin);
freopen("secret.out", "w", stdout);
int x = 0; ans = n = read();
for (int i = 1; i <= n; ++i) {
int y = read();
s[(y-x+7)%7]++; x = y;
}
ans -= s[0]; s[0] = 0;
x = min(s[1], s[6]); ans -= x; s[1] -= x; s[6] -= x;
x = min(s[2], s[5]); ans -= x; s[2] -= x; s[5] -= x;
x = min(s[3], s[4]); ans -= x; s[3] -= x; s[4] -= x;
for (int i = 1; i <= 6; ++i)
if (s[i]) a[++cnt] = i;
memset(g, 0xcf, sizeof(g)); g[0][0][0] = 0;
for (int i = 0; i <= s[a[1]]; ++i) {
memcpy(f, g, sizeof(g));
memset(g, 0xcf, sizeof(g));
for (int j = 0; j <= s[a[2]]; ++j) {
for (int k = 0; k <= s[a[3]]; ++k) {
for (int l = 0; l <= 6; ++l) {
int x = (l + a[1]) % 7;
g[j][k][x] = max(g[j][k][x], f[j][k][l] + !x);
x = (l + a[2]) % 7;
f[j+1][k][x] = max(f[j+1][k][x], f[j][k][l] + !x);
x = (l + a[3]) % 7;
f[j][k+1][x] = max(f[j][k+1][x], f[j][k][l] + !x);
}
}
}
}
for (int i = 0; i <= 6; ++i)
mx = max(mx, f[s[a[2]]][s[a[3]]][i]);
printf("%d\n", ans - mx);
return 0;
}
C 老洪的数组
题目大意 : 二维数组,第一行给定,其他的是上面的加左面的,动态修改第一行的某个数,询问一个20行以内的数是什么
- 把询问分成k组,每一组开始的时候把所有的数预处理出来,然后考虑这组询问里的修改造成的影响,时间复杂度 \(O(\sqrt n)\)
Code
Show Code
#include <cmath>
#include <cstdio>
using namespace std;
const int N = 1e5 + 25, M = 1e9 + 7;
int read(int x = 0, int f = 1, char c = getchar()) {
for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = -1;
for (; c >='0' && c <='9'; c = getchar()) x = x * 10 + c - '0';
return x * f;
}
int n, m, sz, f[21][N], fac[N], inv[N], a[N], b[N], cnt;
int Pow(int a, int k, int ans = 1) {
for (; k; k >>= 1, a = 1ll * a * a % M)
if (k & 1) ans = 1ll * ans * a % M;
return ans;
}
void Init(int n) {
fac[0] = 1;
for (int i = 1; i <= n; ++i)
fac[i] = 1ll * fac[i-1] * i % M;
inv[n] = Pow(fac[n], M - 2);
for (int i = n; i >= 1; --i)
inv[i-1] = 1ll * inv[i] * i % M;
}
int C(int n, int m) {
return 1ll * fac[n] * inv[m] % M * inv[n-m] % M;
}
int main() {
freopen("array.in", "r", stdin);
freopen("array.out", "w", stdout);
n = read(); m = read();
sz = sqrt(80 * n); Init(n + 20);
for (int i = 1; i <= n; ++i)
f[0][i] = read(), a[i] = -1;
for (int t = 1; t <= m; ++t) {
if (t % sz == 1) {
for (int i = 1; i <= cnt; ++i)
f[0][b[i]] = a[b[i]], a[b[i]] = -1;
cnt = 0;
for (int i = 1; i <= 20; ++i)
for (int j = 1; j <= n; ++j)
if ((f[i][j] = f[i-1][j] + f[i][j-1]) >= M) f[i][j] -= M;
}
int od = read(), x = read(), y = read();
if (od == 2) {
int ans = f[x][y];
for (int i = 1; i <= cnt; ++i)
if (x && y >= b[i]) ans = (ans + 1ll * C(x - 1 + y - b[i], x - 1) * (a[b[i]] - f[0][b[i]] + M)) % M;
printf("%d\n", ans);
}
else if (a[x] != -1) a[x] = y;
else ++cnt, b[cnt] = x, a[x] = y;
}
return 0;
}