七夕祭
逆天神题七夕祭!好吧,来看看这个七夕祭......
首先我们觉得很眼熟。其次就想到行列可以分开计算。没错吧?
然后我们就只考虑一种情况就行了:
有个环,环上有若干数。
若一次只能给邻居1,那么至少要多少次?
链上的很好解决。递推即可。环上呢?
首先考虑的是复制为双倍链,发现并没有用。
枚举显然是不行的。
这一个性质我们显然都知道:一定有相邻的两位置之间可以不交换而达到最优解。
但是怎么找他们呢?
数学推导!所以我说这题有点神了。
考虑链上的次数:
把每个位置的数都减去平均数之后,总次数就是:
∑abs(sum[i]) (思考一下为什么)
没错,这里的sum是前缀和。
可知sum[n] = 0;
再来看环:
考虑在第k个人那里断开。
那么答案就是abs(sum[k+1] - sum[k]) + ... + abs(sum[n] - sum[k]) + abs(sum[n]+sum[1]-sum[k]) + ... + abs(sun[n]+sum[k]-sum[k])
由于sum[n]为0,又由于绝对值的性质,我们可以将上式变形如下:
∑abs(sum[i] - sum[k])
要使上式最小,显然问题再一次转化:
面对数轴上若干数,求k使得∑abs(sum[ai] - sum[k]) 最小
考虑我们随机选了一个k。
k左边有p个数,k右边有q个。
我们把k稍微左右移动,就会发现k取在中位数上是坠吼滴。
然后这道综合题就被秒了。
注意:
long long 大法好。
注意:
写法要小心,我把求中位数k写成一个函数就直接爆0,写在原来的solve里就A了。
玄学代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 #include <algorithm> 3 #include <cmath> 4 using namespace std; 5 typedef long long LL; 6 const LL N = 100010; 7 LL x, y, p[2][N], temp[N], sum[N]; 8 9 LL solve(bool f, LL k) { 10 LL n = f ? y : x; 11 for(LL i = 1; i <= n; i++) { 12 temp[i] = p[f][i] - k; 13 sum[i] = sum[i-1] + temp[i]; 14 } 15 sort(sum + 1, sum + n + 1); 16 LL mid = sum[(n+1)>>1], ans = 0; 17 for(LL i = 1; i <= n; i++) { 18 ans += abs(sum[i] - mid); 19 } 20 return ans; 21 } 22 23 int main() { 24 25 LL k, xx, yy; 26 scanf("%lld%lld%lld", &x, &y, &k); 27 for(LL i = 1; i <= k; i++) { 28 scanf("%lld%lld", &xx, &yy); 29 p[0][xx]++; 30 p[1][yy]++; 31 } 32 LL ans = 0; 33 LL step = 0; 34 if(k % x == 0) { 35 ans++; 36 step += solve(0, k/x); 37 } 38 if(k % y == 0) { 39 ans += 10; 40 step += solve(1, k/y); 41 } 42 if(!ans) { 43 printf("impossible"); 44 return 0; 45 } 46 if(ans == 1) { 47 printf("row %lld", step); 48 return 0; 49 } 50 if(ans == 10) { 51 printf("column %lld", step); 52 return 0; 53 } 54 printf("both %lld", step); 55 return 0; 56 } 57 58 /** 59 qi xi ji ! 60 tyvj 1924 61 codevs 2485 62 bzoj 3032 limited 63 */