20zr暑期AB班十连测day4
自己抄自己
C
50pts直接for就for过去了吧
100pts考虑选取一个素数p,然后mod p意义下只会有n^2/p个
然后先for第一个点,再for第二个点的第一维,然后就能算出y坐标是多少了,再去查表,看那个点是哪个点
你可以发现我们能够exgcd去解决,对于一个点,首先解出一个最小解,然后我们从这个解上去+a'找看有没有对应的,花费总解数solve,如果解很多的放入一个vector中就暴力一下
你会发现由于只有1e9所以很快的,大部分解都很少
B
很套路?分段权值为逆序对数+W
猜一下有决策单调性
结果我们没法在O(1)时间算出逆序对,其实可以莫队?的思路
决策单调性:单调栈/队列上二分
CDQ分治+决策单调性二分
决策单调性二分里面的操作可以参考CF868F Yet Another Minimization Problem
就能做到O(nlog^3n)
如果可以离线可以O(\sqrt n)修改O(1)分块
值域分块就是首先我们处理出每个块内部的前缀和,那么查询时块块之间前缀和+块内前缀和
然后强制在线可以可持久化分块,空间复杂度是带根号的,绝对完蛋啊
代码细节:移动端点的时候我们对于一个逆序对只在大的时候计算,work的时候找到区间最优决策点要另开变量因为不一定能update
code:
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) (x&(-x))
#define ll long long
const int MAXN = 5e5 + 7;
int n, X, a[MAXN], q, p;
ll f[MAXN], b[MAXN], res;
inline void add(int x, int y) {
for(; x <= n; x += lowbit(x)) {
b[x] += y;
}
return ;
}
inline void chkmin(ll &x, ll y) {
if(x > y)x = y;
}
inline ll query(int x) {
ll ret = 0;
for(; x; x -= lowbit(x)) {
ret += b[x];
}
return ret;
}
inline ll get(int l, int r) {
// printf("%d %d %d %d %d\n", l, r, p, q, res);
while(q < r) {
++q;
res += q - p - query(a[q]);
// printf("%d %d\n", q - p, query(a[q]));
add(a[q], 1);
}
while(p > l) {
--p;
res += query(a[p]);
add(a[p], 1);
}
while(q > r) {
add(a[q], -1);
res -= q - p - query(a[q]);//q>r,q变小,我们在大的时候计算逆序对
q--;
}
while(p < l) {
add(a[p], -1);
res -= query(a[p]);
p++;
}
return res;
}
inline void work(int l, int r, int L, int R) {//解决l,r的答案问题,利用L,R的决策点
// if(l == r) {
// for(int i = L; i <= R; ++i) {
// chkmax(f[l], f[i] + get(i + 1, l));
// }
// return;
// }
// if(L == R) {
// for(int i = l; i <= r; ++i) {
// chkmax(f[i], f[L] + get(L + 1, i));
// }
// return ;
// }
// printf("qwq - >:l%d r%d L%d R%d\n", l, r, L, R);
// for(int i = L; i <= R; ++i) {
// printf("%d ", f[i]);
// }
// puts("");
int mid = (l + r) >> 1, pos = -1;
ll minx = 1e18;
for(int i = L; i <= R; ++i) {
ll tmp = f[i] + get(i + 1, mid) + X;
// printf("%d?%d\n", tmp, f[mid]);
if(tmp < minx) {
minx = tmp;
pos = i;
}
}
chkmin(f[mid], minx);
assert(~pos);
if(l < mid)work(l, mid - 1, L, pos);
if(r > mid)work(mid + 1, r, pos, R); //决策点单调不减
}
inline void solve(int l, int r) {
if(l == r)return ;
int mid = (l + r) >> 1;
// printf("solve:%d?%d\n", l, r);
solve(l, mid);
work(mid + 1, r, l, mid);
solve(mid + 1, r);
}
int main() {
scanf("%d%d", &n, &X);
for(int i = 1; i <= n; ++i)scanf("%d", &a[i]);
memset(f, 0x3f3f3f3f, sizeof(f));
f[0] = 0;
// printf("%lld?%lld\n", f[1], f[2]);
p = 1, q = 0;
solve(0, n);
printf("%lld\n", f[n]);
return 0;
}
A
带 花 树
路径简单,而且颜色交替,而且端点不同,两两不交,端点标记,权值和最大
要么带权匹配,要么带权拟阵交
相当于拆下点然后路径要从黑到白从白到黑,而且两个点之间有一个被选用
并且我们对于两个拆出的点之间连一条边权为0的边
然后对于两个颜色不同的拆点,是一个端点,我们可以再拆出一个新建点来把
点权变成边权
然后对于那个新点连向的那两个拆点的边的边权就是那个点的点权
新建的点两两连边连成完全图,防止我们没有用上而没有完备匹配
然后如果有奇数个标记点我们就再额外开一个新建点,使得这个图有完美匹配
为什么要完备匹配?因为最大权只有完备匹配