HolyK学长的杂题选讲
HDU6969
先把每个数的指数都模2,然后分两种情况讨论三元组。
如果三元组中存在一个数含有一个质数大于\(\sqrt M\),那么必定有一个另外一个数配对,且只可能含有一个这样的质数。
则枚举大质数\(p\),暴力枚举\(\frac {M}p\)的部分,复杂度是:
可以通过。
否则三元组中三个数都小于等于\(\sqrt M\),则我们通过爆搜可以发现,这样的三元组个数很少,只有1584455个。
那么直接枚举每种三元组即可。
整体复杂度在\(O(M\sqrt M)\)左右。
HDU6974
考虑由于最多只能选出m条路径,令每条路径权值变为\(w_i'=m*10^6+10^6-w_i\),那么变成了最大化权值和的问题,因为选出的路径不会超过m条,所以必定不会进位。
考虑一个线性规划的模型,\(x_i\)表示第\(i\)条路径是否选择,则有:
设\(A\)为点对应到路径的矩阵,则有
考虑转成一个对偶问题,则有:
这个问题即构造点权使得每条路径上的点权和大于等于这条路径的权值,且令点权和最小。
则这个问题很简单,贪心即可,每条链扔到lca上,每次到当前的链,查询链和,判断是否够用,不够用就给lca加权即可。
单点修改链查询,容易改为子树修改,单点查询,复杂度\(O(n\log n)\)
HDU6989
本质上是求这个:
将询问离线,则问题变成,每次加进来一个r,维护每个l的答案。
考虑最大值的情况,最小值同理。
当前这个r往前跳,找到第一个大于等于他的位置i,则\([i+1,r]\)这段区间最大值全部更新为\(a_i\),则这是很经典的连续段问题,我们直接线段树维护历史区间和即可。
复杂度\(O(n\log n)\)。
HDU6991
设\(f_i\)表示以第\(i\)个位置结尾,不考虑后面的极长上升子序列个数。
则考虑从\(j\)转移到\(i\)的条件
那么我们考虑按照\(a_i\)权值从小到大加入,对于每一个已加入的位置,维护一个\(next_i\)表示已加入的数中右边第一个比它大的。
则我们每一次加入一个数,相当于对一段区间取min,且所有能取到min的位置,我们对他的f求和,相当于边取min,便求和。
则用吉老师线段树即可,维护最大值,次大值,最大值dp和即可,复杂度\(O(n\log n)\)。
当然,也有一个比较显然的分治做法,不讲了。
HDU7020
对每种权值,我们单独考虑,则对于我们考虑把当前这种元素赋值为1,其余元素赋值为-1,则相当于求有多少个区间满足区间和大于0。
设\(f_i\)表示前缀和为i的前缀当前有多少个。
对于一段连续的-1,假设他之前的前缀和为\(tot\),则我们相当于求:\(\sum_{i=tot-l}^{tot-1}\sum_{j=min}^{i-1}f_j\),并且将\(i\in[tot-l,tot-1]\)的\(f_i\)加一,其中\(min\)为之前前缀和的最小值。
容易发现,一旦前缀和开始小于等于之前的min,那么接下来的\(-1\)我们就都不用处理了。
考虑维护\(\Delta f_i\),令\(f_i=\sum_{min}^i\Delta f_i\),那么我们考虑维护四个值,一是当前的\(cnt=f_i\),二是当前的\(s=\sum_{j=min}^if_j\),三是当前的答案,四是当前的\(min\)。
则每一次,对于一段对于一段连续的\(-1\),我们考虑暴力往下枚举,一旦低于了min,后面的必定就没有用了,直接令\(cnt=s=0\)即可,每一次移动都是一格的移动,因此,很容易通过\(\Delta f_i\)来维护\(f_i\)。
则我们考虑这样子做的复杂度,每一次min的减少,会导致某些-1枚举的复杂度减少,容易发现,\(min\)必定小于\((1的个数)-(-1的个数)\),则-1总共枚举的复杂度就是:
\((-1的个数)+min=(1的个数)\),则可以发现复杂度就是\(O(1的个数)\)。
那么对于每一种元素都做一次,复杂度是\(O(n)\)。
由于这题有些细节,还是贴个代码吧:
#include <bits/stdc++.h>
#define I inline
#define fi first
#define se second
#define LL long long
#define mp make_pair
#define reg register int
#define pii pair<int,int>
#define fo(i, a, b) for(int i = a; i <= b; i++)
#define fd(i, a, b) for(reg i = a; i >= b; i--)
#define ULL unsigned long long
#define cr const reg&
using namespace std;
const int inf = 2147483647;
const int mod = 998244353;
const int N = 1e6 + 1;
I int _max(cr x, cr y) {return x > y ? x : y;}
I int _min(cr x, cr y) {return x < y ? x : y;}
I LL read() {
LL x = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + (ch ^ 48), ch = getchar();
return x * f;
}
I void ptt(LL x) {if(x >= 10) ptt(x / 10); putchar(x % 10 + '0');}
I void put(LL x) {x < 0 ? putchar('-'), ptt(-x) : ptt(x);}
I void pr1(LL x) {put(x), putchar(' ');}
I void pr2(LL x) {put(x), puts("");}
I int pow_mod(reg a, reg k) {reg ans = 1; for(; k; k >>= 1, a = (LL)a * a % mod) if(k & 1) ans = (LL)ans * a % mod; return ans;}
int a[N], f[N * 2];
vector<int> q[N], cl;
int now, mn, cnt, lst = 0; LL s, ans;
I void add(int l, int r) {f[l]++, f[r + 1]--, cnt++, cl.push_back(l), cl.push_back(r + 1);}
I void down(int x) {
fo(i, 0, x - 1) {
if(now == mn) {
now = mn = mn - (x - i);
cnt = s = 0;
return ;
} cnt -= f[now--], s -= cnt, ans += s;
}
}
int main() {
int T = read();
while(T--) {
int n = read(), mx = 0;
fo(i, 1, n) {
a[i] = read(), mx = max(mx, a[i]);
q[a[i]].push_back(i);
} ans = 0;
fo(i, 0, mx) {
now = mn = n, cnt = s = lst = 0; add(n, n);
fo(j, 0, (int)q[i].size() - 1) {
if(q[i][j] - lst > 1) {
down(q[i][j] - lst - 1);
add(now, now + q[i][j] - lst - 1 - 1);
} s += cnt, cnt += f[++now], ans += s;
add(now, now), lst = q[i][j];
} if(lst < n) down(n - lst);
fo(j, 0, (int)cl.size() - 1) f[cl[j]] = 0;
cl.clear(), q[i].clear();
} pr2(ans);
}
return 0;
}