1. 程式人生 > >【BZOJ 3032】 七夕祭

【BZOJ 3032】 七夕祭

HP target return www. sca ems fine spa imp

【題目鏈接】

https://www.lydsy.com/JudgeOnline/problem.php?id=3032

【算法】

交換左右兩個相鄰格子的攤點,不會改變這一行的攤點個數

交換上下兩個相鄰格子的攤點,不會改變這一列的攤點個數

因此,題目中所要求的兩個問題是獨立的,可以分別計算,以第一問 : 每列攤點個數相等,進行討論 :

我們將每列中的初始攤點個數記為Ai,如果感興趣的攤點總數不能被M整除,則無解,否則,等價於一個“環形均分紙牌”的問題,

如果不允許“環形傳遞”,那麽最少移動步數為 sigma( | Si | ),Si為Ai - T / M的前綴和

如果允許,仔細思考後會發現,一定有一種最優解的方案,使得環上兩個人不進行傳遞,考慮將環斷開 :

假設斷開位置為k,那麽斷開後,每個人的紙牌個數和前綴和分別為 :

Ak+1 Sk+1 - Sk

Ak+2 Sk+2 - Sk

...

Am Sm - Sk

A1 S1 + Sm - Sk

...

Ak Sk + Sm - Sk

因為Sm = 0,所以,前綴和數組與一般情況的差別就是每個位置都減了Sk

所以若斷開位置為k,最少交換次數為 : sigma( | Si - Sk| ) , 當Sk取前綴和數組S的中位數時,這個式子的值最小,也就是說,我們將S數組從小到大排序,取中位數Sk就是最優解

那麽,這道題就迎刃而解了!

【代碼】

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100010

struct pos
{
        int x,y;
} a[MAXN];

int i,N,M,T;
long long ans;
long
long s[MAXN]; inline void solve1() { int i; memset(s,0,sizeof(s)); for (i = 1; i <= T; i++) s[a[i].y]++; for (i = 1; i <= M; i++) s[i] -= T / M; for (i = 2; i <= M; i++) s[i] += s[i-1]; sort(s+1,s+M+1); for (i = 1; i <= M; i++) ans += abs(s[i] - s[(M+1)>>1]); } inline void solve2() { int i; memset(s,0,sizeof(s)); for (i = 1; i <= T; i++) s[a[i].x]++; for (i = 1; i <= N; i++) s[i] -= T / N; for (i = 2; i <= N; i++) s[i] += s[i-1]; sort(s+1,s+N+1); for (i = 1; i <= N; i++) ans += abs(s[i] - s[(N+1)>>1]); } int main() { scanf("%d%d%d",&N,&M,&T); for (i = 1; i <= T; i++) scanf("%d%d",&a[i].x,&a[i].y); if (T % N != 0 && T % M != 0) { printf("impossible"); return 0; } if (T % M == 0 && T % N != 0) { printf("column "); solve1(); } else if (T % M != 0 && T % N == 0) { printf("row "); solve2(); } else { printf("both "); solve1(); solve2(); } printf("%lld\n",ans); return 0; }

【BZOJ 3032】 七夕祭