【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);
}
}