2025.5.26【ZR NOI模拟赛 T2】草莓函数 题解(性质,二分图最大权匹配)
算是比较好玩的题吧,虽然感觉不太可能考。
但是我考试时真是一点思路都没有啊啊啊啊啊啊。。。。
记录一下。
题意
给定 n n n 对整数 ( x i , y i ) (x_i, y_i) (xi,yi) 和一个整数 C C C,求一个定义域和值域都是 Z \Z Z 的函数 f ( x ) f(x) f(x),满足 ∀ x ∈ Z \forall x \in \Z ∀x∈Z, f ( x ) + C = f ( 2 f ( x ) − x + 1 ) f(x) + C = f(2f(x) - x + 1) f(x)+C=f(2f(x)−x+1),使得 ∑ i = 1 n ∣ f ( x i ) − y i ∣ \sum\limits_{i = 1}^{n}|f(x_i) - y_i| i=1∑n∣f(xi)−yi∣ 最小。
1 ≤ n ≤ 10 4 , 1 ≤ C ≤ 200 , 1 ≤ x i , y i ≤ 10 9 1 \leq n \leq 10^4, 1 \leq C \leq200, 1 \leq x_i, y_i \leq 10^9 1≤n≤104,1≤C≤200,1≤xi,yi≤109。
分析
首先要明白这个函数取最优可能是没有解析式的。因为假设有解析式那么这个函数只能是 1 1 1 次函数,并且 k = 1 , b = C − 1 2 k = 1, b = \frac{C - 1}{2} k=1,b=2C−1。然后你会发现 C C C 是偶数的时候你甚至构造不出来一个合法的一次函数满足值域是 Z \Z Z。然后就倒闭了。。
那么我们将它理解成 你要给每个 f ( x ) f(x) f(x) 赋一个值,满足上述限制,并且使所求式最小。
那么怎样赋值才能满足限制呢?先来对函数推一些性质。
性质: ∀ x ∈ Z , f ( x + 2 C ) = f ( x ) + 2 C \forall x \in Z, f(x + 2C) = f(x) + 2C ∀x∈Z,f(x+2C)=f(x)+2C。
证明:
令 f ( x ) = t f(x) = t f(x)=t,有 t + C = f ( 2 t − x + 1 ) t + C = f(2t - x + 1) t+C=f(2t−x+1)。
迭代: f ( 2 t − x + 1 ) + C = f ( 2 ( t + C ) − ( 2 t − x + 1 ) + 1 ) = f ( x + 2 C ) f(2t - x + 1) + C = f(2(t + C) - (2t - x + 1) + 1) = f(x + 2C) f(2t−x+1)+C=f(2(t+C)−(2t−x+1)+1)=f(x+2C)。
所以 f ( x + 2 C ) = t + 2 C = f ( x ) + 2 C f(x + 2C) = t + 2C = f(x) + 2C f(x+2C)=t+2C=f(x)+2C。
得证。
因此如果将 x % 2 C x \% 2C x%2C 相同的 x x x 分到同一类,那么只有一类里定了一个数的 f ( x ) f(x) f(x),其它的 f ( x ) f(x) f(x) 也都随之确定,并且斜率为 1 1 1。
不妨考虑确定 x ∈ [ 0 , 2 C ) x \in [0,2C) x∈[0,2C) 的所有 f ( x ) f(x) f(x),但是这些值是可以任意定的吗?
考虑如果定下了一个 f ( x ) = t f(x) = t f(x)=t,那么 2 t − x + 1 2t - x + 1 2t−x+1 所在那一类也一定会被确定。根据刚才的性质,从 x x x 的等价类出发迭代两次还会回到 x x x 的等价类上。因此再往后迭代一次确定的点所在等价类就是 x , 2 t − x + 1 , x , 2 t − x + 1 , … x, 2t - x + 1, x, 2t - x + 1,\dots x,2t−x+1,x,2t−x+1,…。我们发现它们形成了一个闭环,因此限制就完全转化成了每次同时确定两个等价类,其余等价类迭代一次都不能跑到这两个等价类上。这相当于一个 匹配模型。
观察到 C C C 比较小,所以等价类的数量很少。因此我们有一个初步思路:计算两个等价类 x , y x, y x,y 匹配所产生的最小代价 w ( x , y ) w(x, y) w(x,y),然后在 x , y x, y x,y 之间连一条权值为 w ( x , y ) w(x, y) w(x,y) 的边,最后跑一遍 最小权完美匹配 即可。
这里的 w ( x , y ) w(x, y) w(x,y) 是指确定 x , y x, y x,y 两个等价类的每个点值后对所求式子的最小贡献。
但是这是 一般图最小权最大匹配。一般图最大匹配 都需要用 带花树 才能做。这是我们不可能会的科技。
接着推一下式子:
令 f ( x ) = t f(x) = t f(x)=t,那么 f ( 2 t − x + 1 ) = t + C f(2t - x + 1) = t + C f(2t−x+1)=t+C
我们注意到 x + ( 2 t − x + 1 ) = 2 t + 1 x + (2t - x + 1) = 2t + 1 x+(2t−x+1)=2t+1 是奇数,并且 ( 2 t − x + 1 ) % 2 C (2t -x+1) \% 2C (2t−x+1)%2C 不会改变 2 t − x + 1 2t - x + 1 2t−x+1 的奇偶性,因此 x x x 只会和一个与它奇偶性不同的等价类匹配!!
那么问题就变成了 二分图最小权最大匹配,只需要 E K EK EK 跑 最小费用最大流 即可。
最后剩下一个问题,如何求 w ( x , y ) w(x, y) w(x,y)?
首先设 a a a 的等价类中包含 m m m 个 { ( x i , y i ) } \{(x_i, y_i)\} {(xi,yi)},满足 x i % 2 C = a x_i \% 2C = a xi%2C=a。
设 x i = k i × 2 C + a x_i = k_i \times 2C + a xi=ki×2C+a,那么有:
∑ i = 1 m ∣ f ( x i ) − y i ∣ = ∑ i = 1 m ∣ f ( k i × 2 C + a ) − y i ∣ = ∑ i = 1 m ∣ f ( a ) + k i × 2 C − y i ∣ = ∑ i = 1 m ∣ f ( a ) − ( y i − k i × 2 C ) ∣ \sum\limits_{i = 1}^{m}|f(x_i) - y_i| = \sum\limits_{i = 1}^{m}|f(k_i \times 2C + a) - y_i| = \sum\limits_{i = 1}^{m}|f(a) + k_i \times 2C -y_i| = \sum\limits_{i = 1}^{m}|f(a) - (y_i - k_i \times 2C)| i=1∑m∣f(xi)−yi∣=i=1∑m∣f(ki×2C+a)−yi∣=i=1∑m∣f(a)+ki×2C−yi∣=i=1∑m∣f(a)−(yi−ki×2C)∣。
这看起来像是数轴上选一个点到其它点的距离和最小,结论是选中位数最优。
但是由于 f ( x ) f(x) f(x) 的选择后要匹配上 y y y,因此还要列一些关系式:
令 f ( x ) = t f(x) = t f(x)=t,则 2 t − x + 1 ≡ y ( m o d 2 C ) 2t - x + 1 \equiv y \pmod {2C} 2t−x+1≡y(mod2C)
2 t − x + 1 = y + 2 n C ( n ∈ Z ) 2t - x + 1 = y + 2nC(n \in \Z) 2t−x+1=y+2nC(n∈Z)
t = n C + x + y − 1 2 ( n ∈ Z ) t = nC + \frac{x + y - 1}{2}(n \in \Z) t=nC+2x+y−1(n∈Z)
f ( x ) = n C + x + y − 1 2 ( n ∈ Z ) f(x) = nC + \frac{x + y - 1}{2}(n \in \Z) f(x)=nC+2x+y−1(n∈Z)
这相当于初始 f ( x ) f(x) f(x) 在 x + y − 1 2 \frac{x + y - 1}{2} 2x+y−1,你可以朝左右跳任意步,步长为 C C C。那么显然还是跳到中位数附近 O ( 1 ) O(1) O(1) 个位置取到最优。
但是别忘了 t t t 的赋值同样影响着 y y y 等价类的贡献,我们来看看 f ( y ) f(y) f(y) 的表达式:
f ( 2 t − x + 1 ) = t + C f(2t - x + 1) = t +C f(2t−x+1)=t+C
f ( y + 2 n C ) = f ( y ) + 2 n C = t + C = n C + x + y − 1 2 + C f(y + 2nC) = f(y) + 2nC = t + C = nC + \frac{x + y - 1}{2} + C f(y+2nC)=f(y)+2nC=t+C=nC+2x+y−1+C
f ( y ) = x + y − 1 2 + C − n C f(y) = \frac{x + y - 1}{2} + C - nC f(y)=2x+y−1+C−nC
f ( y ) + f ( x ) = x + y + C − 1 f(y) + f(x) = x + y + C - 1 f(y)+f(x)=x+y+C−1, f ( y ) = ( x + y + C − 1 ) − f ( x ) f(y) = (x + y + C - 1) - f(x) f(y)=(x+y+C−1)−f(x)
然后变量就被化成一个了。我们将 f ( y ) f(y) f(y) 带入它的贡献式:
∑ i = 1 m y ∣ f ( y ) − ( y i − k i × 2 C ) ∣ = ∑ i = 1 m y ∣ f ( x ) − ( x + y + C − 1 − ( y i − k i × 2 C ) ) ∣ \sum\limits_{i = 1}^{m_y}|f(y) - (y_i - k_i \times 2C)| = \sum\limits_{i = 1}^{m_y}|f(x) - (x+y+C - 1 -(y_i - k_i \times 2C))| i=1∑my∣f(y)−(yi−ki×2C)∣=i=1∑my∣f(x)−(x+y+C−1−(yi−ki×2C))∣
那么只需要将 y y y 等价类中的点 ( x i , y i ) (x_i, y_i) (xi,yi): x + y + C − 1 − ( y i − k i × 2 C ) x + y + C - 1 - (y_i - k_i \times 2C) x+y+C−1−(yi−ki×2C) 也加入数轴中,求出最优位置即可。
每次只有 O ( 1 ) O(1) O(1) 个位置需要 c h e c k check check,每个等价类中的点一共被枚举 C C C 次,总复杂度 O ( n C ) O(nC) O(nC)。
总复杂度 O ( n C + 费用流复杂度 ) O(nC + 费用流复杂度) O(nC+费用流复杂度),可过。
CODE:
// 最小费用最大流
#include<bits/stdc++.h>
#define pb emplace_back
using namespace std;
const int N = 1e4 + 10;
const int M = 210;
const int INF = 1e8;
typedef long long LL;
int n, C, len;
LL x[N], y[N], w[M * 2][M * 2];
LL num[N];
vector< int > node[M * 2];
inline LL calc(LL x) {LL ret = 0;for(int i = 1; i <= len; i ++ ) ret += abs(num[i] - x);return ret;
}
inline LL up(LL x, int s) {if(s >= x) return s - (s - x) / C * C;else return s + ((x - s - 1) / C + 1) * C;
}
inline LL down(LL x, int s) {LL p = up(x, s);if(p == x) return p;else return p - C;
}
inline LL solve(int a, int b) { // f(a) 应取中位数 len = 0; for(auto v : node[a]) num[++ len] = (y[v] - (x[v] - a));for(auto v : node[b]) num[++ len] = (a + b + C - 1 - (y[v] - (x[v] - b)));sort(num + 1, num + len + 1);int p = (len + 1) / 2; LL res = 2e18;for(int i = p; i <= ((len & 1) ? p : p + 1); i ++ ) {if(i > len) break;if((len & 1) || (i == p + 1)) res = min(res, calc(down(num[i], (a + b - 1) / 2)));if((len & 1) || (i == p)) res = min(res, calc(up(num[i], (a + b - 1) / 2)));}return res;
}
int S, T, nd[M * 2], idx, pre[M * 2];
LL incf[M * 2], d[M * 2];
struct edge {int v, last, c; LL w;
} E[M * M * 2 + 8 * M];
int head[M * 2], tot;
bool vis[M * 2];
inline void add(int u, int v, int c, LL w) {E[tot] = (edge) {v, head[u], c, w}; head[u] = tot ++;E[tot] = (edge) {u, head[v], 0, -w}; head[v] = tot ++;
}
inline bool bfs(int s, int t) {memset(d, 0x3f, sizeof d); memset(incf, 0, sizeof incf);d[s] = 0, incf[s] = INF; queue< int > q; q.push(s); vis[s] = 1;while(!q.empty()) {int u = q.front(); q.pop(); vis[u] = 0;for(int i = head[u]; ~i; i = E[i].last) {int v = E[i].v;if(E[i].c && d[u] + E[i].w < d[v]) {d[v] = d[u] + E[i].w;incf[v] = min(incf[u], 1LL * E[i].c);pre[v] = i;if(!vis[v]) vis[v] = 1, q.push(v);}}}return incf[t] > 0;
}
inline LL EK(int s, int t) {int flow = 0; LL cost = 0;while(bfs(s, t)) {flow += incf[t]; cost += d[t] * incf[t];for(int u = t; u != s; u = E[pre[u] ^ 1].v) {E[pre[u]].c -= incf[t]; E[pre[u] ^ 1].c += incf[t];}}return cost;
}
int main() {scanf("%d%d", &n, &C);for(int i = 1; i <= n; i ++ ) {scanf("%lld%lld", &x[i], &y[i]);node[x[i] % (2 * C)].pb(i);}for(int x = 0; x < 2 * C; x ++ ) for(int y = x + 1; y < 2 * C; y += 2) // 求 w(x, y) w[x][y] = solve(x, y);S = ++ idx, T = ++ idx;for(int i = 0; i < 2 * C; i ++ ) nd[i] = ++ idx;memset(head, -1, sizeof head);for(int i = 0; i < 2 * C; i += 2 ) add(S, nd[i], 1, 0);for(int i = 1; i < 2 * C; i += 2 ) add(nd[i], T, 1, 0);for(int x = 0; x < 2 * C; x += 2) for(int y = 1; y < 2 * C; y += 2 ) // 偶数往奇数连边 add(nd[x], nd[y], 1, w[min(x, y)][max(x, y)]);printf("%lld\n", EK(S, T));return 0;
}