1. 程式人生 > >【NOIP2013模擬】七夕祭

【NOIP2013模擬】七夕祭

題目描述

七夕節因牛郎織女的傳說而被扣上了「情人節」的帽子。於是TYVJ今年舉辦了一次線下七夕祭。Vani同學今年成功邀請到了cl同學陪他來共度七夕,於是他們決定去TYVJ七夕祭遊玩。
TYVJ七夕祭和11區的夏祭的形式很像。矩形的祭典會場由N排M列共計N×M個攤點組成。雖然攤點種類繁多,不過cl只對其中的一部分攤點感興趣,比如章魚燒、蘋果糖、棉花糖、射的屋……什麼的。Vani預先聯絡了七夕祭的負責人zhq,希望能夠通過恰當地佈置會場,使得各行中cl感興趣的攤點數一樣多,並且各列中cl感興趣的攤點數也一樣多。
不過zhq告訴Vani,攤點已經隨意佈置完畢了,如果想滿足cl的要求,唯一的調整方式就是交換兩個相鄰的攤點。兩個攤點相鄰,當且僅當他們處在同一行或者同一列的相鄰位置上。由於zhq率領的TYVJ開發小組成功地扭曲了空間,每一行或每一列的第一個位置和最後一個位置也算作相鄰。現在Vani想知道他的兩個要求最多能滿足多少個。在此前提下,至少需要交換多少次攤點。

輸入

第一行包含三個整數N和M和T。T表示cl對多少個攤點感興趣。
接下來T行,每行兩個整數x, y,表示cl對處在第x行第y列的攤點感興趣。

輸出

首先輸出一個字串。如果能滿足Vani的全部兩個要求,輸出both;如果通過調整隻能使得各行中cl感興趣的攤點數一樣多,輸出row;如果只能使各列中cl感興趣的攤點數一樣多,輸出column;如果均不能滿足,輸出impossible。
如果輸出的字串不是impossible, 接下來輸出最小交換次數,與字串之間用一個空格隔開。

題面分析

假設滿足每行(列)攤點一樣多,那麼每行該有t/n(t/m)個點。那麼,我們可以由此將row/column/both/impossible判斷出來。設b[i]為第i行該多放多少個點才能到達平均,c[i]則為列。(負數我們也不慫他)很明顯,如果想均分紙牌,呵呵呵。(~那AC離開千里之外,他不再回來~)設 bi 的字首和為 si。如果從第k個位置開始,那麼第i堆和第i+1堆交換的紙牌數就是|si-sk|。總代價就是|s1-sk|+|s2-sk|+|s3-sk|+……+|sn-sk|。(絞盡腦汁冥思苦想ing)
(三年後)啊!!!當k為1~n的中位數時,該式有最小值。。。。。。
那麼我們就可以約掉其中一重迴圈,很好,我會給你一個獎勵,那就是——AC(別想著gunpla之類的,不存在的)。

#include<cstdio>
#include<cmath>
#include<algorithm>
#define FK 100001
using namespace std;
long long n,m,t,a,a1,ans,h[FK],l[FK],s[FK],s1[FK],b[FK],c[FK],k; 
int i,j;
int main()
{
    freopen("a.in","r",stdin);
    scanf("%lld%lld%lld",&n,&m,&t);
    for (i=1;i<=t;i++)
    {
        scanf("%lld
%lld"
,&a,&a1); h[a]++; l[a1]++; } for (i=1;i<=n;i++) { b[i]=h[i]-t/n; s[i]=s[i-1]+b[i]; } sort(s+1,s+n+1); for (i=1;i<=m;i++) { c[i]=l[i]-t/m; s1[i]=s1[i-1]+c[i]; } sort(s1+1,s1+m+1); if (t%n!=0 && t%m!=0) { printf("%s","impossible"); } if (t%n==0 && t%m!=0) { k=(1+n)/2; for (i=1;i<=n;i++) { ans+=abs(s[i]-s[k]); } printf("%s%lld","row ",ans); } if (t%n!=0 && t%m==0) { k=(1+m)/2; for (i=1;i<=m;i++) { ans+=abs(s1[k]-s1[i]); } printf("%s%lld","column ",ans); } if (t%n==0 && t%m==0) { k=(1+n)/2; for (i=1;i<=n;i++) { ans+=abs(s[k]-s[i]); } k=(1+m)/2; for (i=1;i<=m;i++) { ans+=abs(s1[k]-s1[i]); } printf("%s%lld","both ",ans); } }