# Cats Transport
# 题目大意
有 只小猫要在 个山丘上玩耍,雇佣了 个人接它们回来,这些人均在 号山丘出发。山丘 与山丘 的距离是 米,每时刻人们走 米。
小猫 会在山丘 玩耍,并在时间 结束它的游玩,然后在山丘上等着有人来接它。一个人在路过这个山丘时会把在等待的小猫带上(不需要额外的时间)。
我们需要安排每个人的出发时间,使小猫们的等待时间之和最小,输出这个最少的等待时间。
# 解题思路
计算出各只小猫被接到的出发时间 ,然后我们对 排个序,于是题意转化为了有 只猫,每只猫有一个权值 ,如果出发时间大于等于 ,则可以接到第 只猫。设出发时间为 ,则接到第 只猫时,这只猫会等待 的时间。
我们设计状态 表示 轮到第 个人出发,这个人选择了第 只猫的最小等待时间之和。暴力枚举上一个人选择的猫的做法的时间复杂度是 。
记 ,转移方程式如下:
f[i][j] = \max_{j,k}\left\
化简式子得到:
。
然后…… 斜率优化干他。
# Code
#define Aniciry Meteorshower_Y | |
#include<algorithm> | |
#include<iostream> | |
#include<cstring> | |
#include<cstdio> | |
#define long long long | |
using namespace std; | |
const int MAXN = 1e5+10; | |
int n, m, p, l, r, q[MAXN]; | |
long h[MAXN], t[MAXN], d[MAXN]; | |
long a[MAXN], sum[MAXN], f[2][MAXN]; | |
inline auto X(int k) -> long; | |
inline auto Y(int k, int i) -> long; | |
inline auto slope(int a, int b, int i) -> double; | |
auto prework() -> void; | |
int main() | |
{ | |
scanf("%d%d%d", &n, &m, &p); | |
for(int i = 2; i <= n; i += 1) | |
scanf("%lld", &d[i]); | |
for(int i = 1; i <= m; i += 1) | |
scanf("%lld%lld", &h[i], &t[i]); | |
prework(); | |
for(int i = 2, c = 0; i <= p; i += 1, c ^= 1) | |
{ | |
q[1] = 0; l = r = 1; | |
for(int j = 1; j <= m; j += 1) | |
{ | |
while(l < r and slope(q[l], q[l+1], c) < a[j]) l += 1; | |
f[c][j] = f[c^1][q[l]]+a[j]*(j-q[l])-(sum[j]-sum[q[l]]); | |
while(l < r and slope(q[r-1], q[r], c) >= slope(q[r-1], j, c)) r -= 1; | |
q[++r] = j; | |
} | |
} | |
printf("%lld", f[p&1][m]); | |
} | |
auto prework() -> void | |
{ | |
for(int i = 1; i <= n; i += 1) | |
d[i] += d[i-1]; | |
for(int i = 1; i <= m; i += 1) | |
a[i] = t[i]-d[h[i]]; | |
sort(a+1, a+m+1); | |
for(int i = 1; i <= m; i += 1) | |
sum[i] = sum[i-1]+a[i]; | |
for(int j = 1; j <= m; j += 1) | |
f[1][j] = a[j]*j-sum[j]; | |
} | |
inline auto X(int k) -> long{return k;} | |
inline auto Y(int k, int i) -> long{return f[i^1][k]+sum[k];} | |
inline auto slope(int a, int b, int i) -> double{return double(Y(b, i)-Y(a, i))/(X(b)-X(a));} |
# Pairwise Modulo
# 题目描述
给定一个由不同数组成的序列 ,定义 为:
对于每个 ,求出 。
# 解题思路
需要求出每个 ,考虑增量法由上个答案推出当前答案:
取模不好处理,将它转化为相减的形式:
把式子拆开得到:
-
对于 ,直接枚举倍数就好,时间复杂度 .
-
对于 ,考虑每一组值相同的 的贡献。区间加 和 前缀和维护一下。
# Code
#define Aniciry Meteorshower_Y | |
#include<iostream> | |
#include<cstdio> | |
#define lowbit(i) (i&-i) | |
using namespace std; | |
/* 此处省略快读快写 */ | |
#define long long long | |
const int A = 300000; | |
const int MAXN = 3e5+10; | |
int n, a[MAXN]; long ans; | |
long tr1[MAXN], tr2[MAXN]; | |
inline auto query1(int x) -> long; | |
inline auto query2(int x) -> long; | |
inline auto add1(int x, int k) -> void; | |
inline auto add2(int x, int k) -> void; | |
int main() | |
{ | |
read(n); | |
for(int i = 1; i <= n; i += 1) | |
read(a[i]); | |
long sum = 0; | |
for(int i = 1; i <= n; i += 1) | |
{ | |
ans += sum; sum += a[i]; | |
for(int j = a[i]; j <= A; j += a[i]) | |
ans -= j*(query1(min(j+a[i]-1, A))-query1(j-1)); | |
add1(a[i], 1); | |
ans += (i-1ll)*a[i]-query2(a[i]); | |
for(int j = a[i]; j <= A; j += a[i]) add2(j, a[i]); | |
print(ans, ' '); | |
} | |
} | |
inline auto add1(int x, int k) -> void | |
{ | |
for(int i = x; i <= A; i += lowbit(i)) | |
tr1[i] += k; | |
} | |
inline auto add2(int x, int k) -> void | |
{ | |
for(int i = x; i <= A; i += lowbit(i)) | |
tr2[i] += k; | |
} | |
inline auto query1(int x) -> long | |
{ | |
long ans = 0; | |
for(int i = x; i; i -= lowbit(i)) | |
ans += tr1[i]; | |
return ans; | |
} | |
inline auto query2(int x) -> long | |
{ | |
long ans = 0; | |
for(int i = x; i; i -= lowbit(i)) | |
ans += tr2[i]; | |
return ans; | |
} |
# Decinc Dividing
# 题目描述
定义一个序列 是好的,仅当可以通过删除 的一个单调递减子序列(可以为空)使得 单调递增。
给定一个 的排列 ,你需要求出 中有多少子段是好的。
# 解题思路
首先,答案是单调的,对于一个合法区间 ,它的子区间一定合法;对于一个非法区间 ,包含它的区间一定非法。
对于每一个位置 ,我们定义 为最大的值满足区间 合法。
考虑分治,对于区间 ,我们先求出 和 ,若
,则将满足 的 都赋值为 ,否则设 ,并递归处理 和 。最后的答案即为 .
# Code
#define Aniciry Meteorshower_Y | |
#include<iostream> | |
#include<cstring> | |
#include<climits> | |
#include<cstdio> | |
using namespace std; | |
const int N = 2e5+10; | |
int n, a[N], f[N][2], rt[N]; | |
long long ans; | |
auto calc(int x) -> void; | |
auto solve(int l, int r) -> void; | |
int main() | |
{ | |
scanf("%d", &n); | |
for(int i = 1; i <= n; i += 1) | |
scanf("%d", &a[i]); | |
calc(1), calc(n); solve(1, n); | |
for(int i = 1; i <= n; i += 1) | |
ans += rt[i]; | |
printf("%lld", ans-n*(n-1ll)/2); | |
return 0; | |
} | |
auto solve(int l, int r) -> void | |
{ | |
if(r-l <= 1) return ; | |
if(rt[l] == rt[r]) | |
{ | |
for(int i = l; i <= r; i += 1) | |
rt[i] = rt[l]; return ; | |
} | |
int mid = (l+r)>>1; calc(mid); | |
solve(l, mid); solve(mid, r); | |
} | |
auto calc(int x) -> void | |
{ | |
f[x][0] = n+1; f[x][1] = 0; | |
for(int i = x+1; i <= n; i += 1) | |
{ | |
f[i][0] = 0; f[i][1] = n+1; | |
if(a[i-1] < a[i]) f[i][0] = f[i-1][0]; | |
if(a[i-1] > a[i]) f[i][1] = f[i-1][1]; | |
if(f[i-1][1] < a[i]) f[i][0] = max(f[i][0], a[i-1]); | |
if(f[i-1][0] > a[i]) f[i][1] = min(f[i][1], a[i-1]); | |
if(f[i][0] == 0 and f[i][1] == n+1) return (void)(rt[x] = i-1); | |
} | |
rt[x] = n; | |
} |