AcWing
  • 首页
  • 课程
  • 题库
  • 更多
    • 竞赛
    • 题解
    • 分享
    • 问答
    • 应用
    • 校园
  • 关闭
    历史记录
    清除记录
    猜你想搜
    AcWing热点
  • App
  • 登录/注册

AcWing 105. 七夕祭    原题链接    困难

作者: 作者的头像   秦淮岸灯火阑珊 ,  2019-01-22 13:22:46 ,  所有人可见 ,  阅读 7518


33


9

题目描述

七夕节因牛郎织女的传说而被扣上了「情人节」的帽子。

于是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, 接下来输出最小交换次数,与字符串之间用一个空格隔开。

数据范围
$1≤N,M≤100000, 0≤T≤min(N∗M,100000), 1≤x≤N, 1≤y≤M$

样例

输入样例:
2 3 4
1 3
2 1
2 2
2 3
输出样例:
row 1

分治+贪心+前缀和+中位数+排序

这道题目有一个非常重要的性质就是,只会改变相邻的两个数的位置,因此我们交换两个数,只会改变一行的喜爱小摊或者一列的喜爱小摊,而不会同时改变行和列的喜爱小摊,既然这样的话,我们就可以将这道题目分成两个部分,一部分是求行的最少次数,一部分是求列的最少次数。
既然如此的话,这道题目就成为了环形的均分纸牌问题,均分纸牌这是一道经典的贪心问题,可以自行百度理解即可懒惰病发作ing 但是环形均分纸牌问题和普通均分纸牌问题又有不同之处,因此我们要截环为序列,所以说我们可以利用中位数把环形变成区间,具体思路可见《算法竞赛进阶指南》。
感谢@sandychn 大佬指出简短代码中一个小问题.

后更新简短代码

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define fir(i,a,b) for(ll i=a;i<=b;i++)
const int N=1e5+10;
ll n,m,t,a[N],b[N],f[N],x,y,rr,cc;
ll calc(ll a[],ll n)
{
    fir(i,1,n)
    {
        a[i]-=(a[0]/n);
        f[i]=f[i-1]+a[i];
    }
    sort(f+1,f+1+n);
    ll mid=(n+1)>>1,ans=0;//不是n|1,因为n若为奇数会出问题.不过这道题目也不会出现问题.因为中位数只要求是最中间,但是如果说我们要求出n+1的话,那么不能用n|1. 感谢@sandychn 大佬指出问题.
    fir(i,1,n)
        ans+=abs(f[mid]-f[i]);
    return ans;
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>n>>m>>t;
    for(int i=1;i<=t;i++)
    {
        cin>>x>>y;
        a[x]++;
        b[y]++;
    }
    fir(i,1,n)
        a[0]+=a[i];
    fir(i,1,m)
        b[0]+=b[i];
    ll as=a[0]%n,bs=b[0]%m;
    if (!as && !bs)
        cout<<"both "<<calc(a,n)+calc(b,m);
    else if(!as)
        cout<<"row "<<calc(a,n);
    else if(!bs)
        cout<<"column "<<calc(b,m);
    else
        cout<<"impossible";
    return 0;
}

lyd老师的精简C++ 代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int u=100010; 
long long b[u],c[u],f[u];
long long n,m,t,i,j,x,y;

long long calc(long long a[u],int n)
{
    long long ans=0; int i;
    for(i=1;i<=n;i++)
    {
        a[i]-=a[0]/n;
        f[i]=f[i-1]+a[i];
    }
    sort(f+1,f+n+1);
    for(i=1;i<=n;i++) ans+=abs(f[i]-f[n+1>>1]);
    return ans;
}

int main()
{
    freopen("tanabata.in","r",stdin);
    freopen("tanabata.out","w",stdout); 
    cin>>n>>m>>t;
    for(i=1;i<=t;i++)
    {
        scanf("%d%d",&x,&y);
        b[x]++,c[y]++; 
    }
    for(i=1;i<=n;i++) b[0]+=b[i];
    for(i=1;i<=m;i++) c[0]+=c[i];
    if(b[0]%n==0&&c[0]%m==0)
        printf("both %lld\n",calc(b,n)+calc(c,m));
    else if(b[0]%n==0)
        printf("row %lld\n",calc(b,n));
    else if(c[0]%m==0)
        printf("column %lld\n",calc(c,m));
    else puts("impossible");
    return 0;
}

蒟蒻我的繁杂代码

#include <bits/stdc++.h>
using namespace std;
const int N=100100;
#define ll long long
ll n,m,t,i,j,k,s[N],a[N],x,y,ans1,ans2,r[N],c[N],rr,cc;
void init()
{
    ios::sync_with_stdio(false);
    cin>>n>>m>>t;
    for (i=1;i<=t;i++)
    {
        int x,y;
        cin>>x>>y;
        r[x]++;
        c[y]++;
        rr++;
        cc++;
    } 
}
bool work1()//横排
{
    if (t%n!=0)
        return 0;
    memset(s,0,sizeof(s));
    rr/=n;
    for (i=1;i<=n;i++)
    {
        r[i]-=rr;
        s[i]=s[i-1]+r[i];
    }
    sort(s+1,s+1+n);
    int k=(1+n)>>1;
    for (i=1;i<=n;i++)
        ans1+=abs(s[i]-s[k]);
    return 1;
}
bool work2()//竖排
{
    if (t%m!=0)
        return 0;
    memset(s,0,sizeof(s));
    cc/=m;
    for (i=1;i<=m;i++)
    {
        c[i]-=cc;
        s[i]=s[i-1]+c[i];
    }
    sort(s+1,s+1+m);
    int k=(1+m)>>1;
    for (i=1;i<=m;i++)
        ans2+=abs(s[i]-s[k]);
    return 1;
}
int main()
{
    init();
    bool ok1=work1(),ok2=work2();
    if (ok1 && ok2)
        cout<<"both";
    else
    if (ok1)
        cout<<"row";
    else if (ok2)
        cout<<"column";
    else 
        cout<<"impossible";
    if (ok1 || ok2)
        cout<<" "<<ans1+ans2;
    return 0;
}

59 评论


用户头像
b-tree   2021-06-09 21:20      4    踩      回复

就离谱, 懒得翻书直接题解, 你跟我说具体思路可见《算法竞赛进阶指南》??
那我要你这题解何用?? ctrl + c . ctrl + v ???

用户头像
Coros_Trusds   2021-06-10 08:17      2    踩      回复

就离谱,学OI还想偷懒?你干脆退役区安享晚年吧??
挑刺挑刺,你妈就是刺。

用户头像
燈光下的凄凉   2021-08-28 13:32      2    踩      回复

真的,人家辛辛苦苦打了半天,你一句话人家的心血都付诸东流了,锤子长在脑壳上,我都奇怪世博会怎么没喊你去展览。。。

用户头像
think_twice   2021-10-17 22:49    回复了 Coros_Trusds 的评论         踩      回复

政治没学过的家伙。。。

用户头像
Coros_Trusds   2021-10-22 20:29    回复了 think_twice 的评论         踩      回复

你觉得你学过是吗,就凭你这句话

用户头像
Coros_Trusds   2021-10-22 20:31    回复了 think_twice 的评论      1    踩      回复

属于那种学了点屁事就出来卖弄的,难听点就是没有搞清楚政治这句话适用的范围,不看你运用的场合。

用户头像
Coros_Trusds   2021-10-22 20:32         踩      回复

不好意思,当时有些幼稚,语气很激烈,非常抱歉。

用户头像
lixiaoqian_qwq   2021-11-20 14:16    回复了 Coros_Trusds 的评论         踩      回复

…

用户头像
月无阴晴   2022-03-01 16:15         踩      回复

lj人

用户头像
Ain00   2022-07-01 15:40      5    踩      回复

虽然这位老哥的评论很nt,但是这题推荐看他的题解

用户头像
AC不了怎么办   2022-07-29 17:39    回复了 Ain00 的评论      9    踩      回复

可他说的是实话吧,题解本就应该解决人们的问题,如果只是将代码罗列出来并没有仔细解释每一步骤,为什么叫题解呢....虽然说的有点激烈hhhh,但对于这件事情还是在理的,只不过表达再转变下就更好了hhhh


用户头像
wh2011   2023-07-13 19:27      1    踩      回复

$lyd$ 是指老 $y$ 的吗


用户头像
你的第一位acsaber老师   2022-05-09 19:52         踩      回复

$qwq$这个题解写的不错,包括了许多不同风格的代码,思路清楚


用户头像
liyx19   2020-12-14 13:33         踩      回复

请问大佬为什么要排f这个前缀和数组啊


用户头像
Omega_   2020-10-16 14:10         踩      回复

请问怎么一开始就知道要用
long long的 我实在是没看出来


用户头像
MournInk   2020-10-04 19:46         踩      回复

Orz


用户头像
幻灵の无翼   2020-07-09 22:46         踩      回复

繁 杂


用户头像
willyboy   2020-06-29 05:36         踩      回复

对于calc函数可以看122糖果传递


用户头像
softsun   2020-04-29 22:55         踩      回复

每行的总和和每列的总和不是t吗?


用户头像
小迷弟   2020-02-25 15:45         踩      回复

dl,我对题目理解有个问题, 1摊位和2摊位相邻, 2摊位还和3摊位相邻, 那么1号摊位和2号摊位交换了位置之后,能否继续和3摊位继续交换位置?


用户头像
xhQYm   2020-02-24 10:47         踩      回复

%%%秦老师


用户头像
zmj2008   2019-11-03 15:31         踩      回复

n已经定义成全局变量了啊
怎么calc函数里又定义了n
不会重名吗???

用户头像
秦淮岸灯火阑珊   2019-11-04 18:23         踩      回复

不会的,函数内部的局部变量等级高于全局变量

用户头像
zmj2008   2019-11-04 20:31    回复了 秦淮岸灯火阑珊 的评论         踩      回复

谢谢大佬

用户头像
秦淮岸灯火阑珊   2019-11-05 21:27    回复了 zmj2008 的评论         踩      回复

您客气了

用户头像
秦淮岸灯火阑珊   2019-11-05 21:27    回复了 zmj2008 的评论         踩      回复

您客气了

用户头像
zmj2008   2019-11-07 20:39    回复了 秦淮岸灯火阑珊 的评论         踩      回复

客气啥


用户头像
无间溢出   2019-08-27 13:50         踩      回复

想问一下,如果最后不是abs(s[i]-s[k])求和,而是ans把前一半s[i]减去,后一半s[i]加上,得到的效果不是一样的吗?

用户头像
秦淮岸灯火阑珊   2019-08-27 15:33         踩      回复

为啥是一样的。。。
我一脸蒙圈。。。。
这个循环是。

for (i=1;i<=m;i++)
        ans2+=abs(s[i]-s[k]);

这个是绝对值求和,您哪个s[i]减去,加去是什么意思?我太菜了,根本看不懂,您说的。。。。

用户头像
无间溢出   2019-08-27 16:43    回复了 秦淮岸灯火阑珊 的评论         踩      回复

for (int i=1; i<=l/2; i)
ans-=s[i];
for (int i=l/2+1+(l&1); i<=l;
i)
ans+=s[i];
哦哦,我是这个意思,刚又交了一遍,居然过了。。。打扰大佬了

用户头像
秦淮岸灯火阑珊   2019-08-27 18:06    回复了 无间溢出 的评论         踩      回复

QwQ,木有事情啦。


用户头像
countryhope   2019-08-05 09:46         踩      回复

为什么这三份代码都又统计了一遍a[0],b[0],不是一定都是T吗

用户头像
秦淮岸灯火阑珊   2019-08-05 14:45         踩      回复

这三份代码都是AC代码的....
为什么会TLE呢?
时间复杂度不是挺好的吗?

用户头像
countryhope   2019-08-05 15:14    回复了 秦淮岸灯火阑珊 的评论         踩      回复

额 , 我的意思是 题目中给的数据 T

用户头像
countryhope   2019-08-05 15:15    回复了 秦淮岸灯火阑珊 的评论         踩      回复

已经给了T(感兴趣的摊位)了 ,为啥还要在统计一遍

用户头像
秦淮岸灯火阑珊   2019-08-05 18:01    回复了 countryhope 的评论         踩      回复

我们计算每一行,每一列感兴趣的摊位,然后进行处理.

用户头像
countryhope   2019-08-05 20:38    回复了 秦淮岸灯火阑珊 的评论         踩      回复

可是它就是等于T的,我试了直接用T ,也过了,是测试数据的事吗?

用户头像
秦淮岸灯火阑珊   2019-08-06 07:16    回复了 countryhope 的评论         踩      回复

数据水了吧…
我记得有无解这回事.


用户头像
冷瞳丶Nemsis   2019-07-20 23:47         踩      回复

dalao 我想问一下为啥这题在计算那个mid的时候不能直接整除2,还是不太明白

用户头像
秦淮岸灯火阑珊   2019-07-21 09:00         踩      回复

因为避免奇数整除2,比如说3/2=1,但是我们要的中位数是2,所以(3+1)/2=2,而假如是4,(4+1)/2=2,依旧是2.

用户头像
冷瞳丶Nemsis   2019-07-21 10:34    回复了 秦淮岸灯火阑珊 的评论         踩      回复

嗷嗷 懂了懂了 谢谢dalao


用户头像
一树十兮秋   2019-02-07 21:58         踩      回复

这里有一个疑问就是最后的答案是从 i =1一直累加到M的前缀和,但是我的想法是 第一个数和第二个数交换后,即第一个前缀和第二个数交换。如果直接在用第二个前缀(即第一个数和第二个数的和)与第三个数进行交换,会不会重复了啦,是否应该把第一个数更新为T/M后在和第二个数相加变成第二个前缀呢?

用户头像
秦淮岸灯火阑珊   2019-02-07 22:23         踩      回复

这里的前缀和,实际上是在求贪心经典模型均分纸牌和中位数性质,而并不是原题目的交换.这一点要切记,我们最后得到的答案,是环形均分纸牌+中位数性质.这一点,书上写得很明确,我的题解也是按照这个思路的.

用户头像
秦淮岸灯火阑珊   2019-02-07 22:23    回复了 秦淮岸灯火阑珊 的评论         踩      回复

calc函数,不是求原问题,而是在求原问题转换后两个模型的答案.


用户头像
sandychn   2019-02-04 17:31         踩      回复

简短代码的calc函数中$mid=(n|1)>>1$不对

用户头像
秦淮岸灯火阑珊   2019-02-04 19:01         踩      回复

怎么了,不就是(n+1)/2吗.这些代码都是我提交AC的.

用户头像
秦淮岸灯火阑珊   2019-02-04 19:01         踩      回复

位运算中n|1相当于n+1

用户头像
sandychn   2019-02-05 14:43    回复了 秦淮岸灯火阑珊 的评论         踩      回复

我知道你的意思。当$n$为奇数时$n|1==n$

用户头像
sandychn   2019-02-05 14:49    回复了 秦淮岸灯火阑珊 的评论         踩      回复

$n$为奇数时,$(n|1)>>1=n>>1=n/2$不对。代码会WA。只是发现有错误,想跟你说一下。

用户头像
秦淮岸灯火阑珊   2019-02-05 15:04    回复了 sandychn 的评论         踩      回复

首先感谢一下,然后我想想啊.

用户头像
秦淮岸灯火阑珊   2019-02-05 15:07    回复了 sandychn 的评论         踩      回复

刚才编辑了代码,发现果然是这这样的,感谢!!!

用户头像
sandychn   2019-02-05 15:35    回复了 秦淮岸灯火阑珊 的评论         踩      回复

没事。这道题直接写(n|1)>>1会WA掉acwing上的数据。奇数应取(n+1)/2,偶数取n/2或(n+1)/2都可以(除法向下取整)。所以(n+1)/2对奇偶都适用。n|1应该只适用于偶数呀。

用户头像
秦淮岸灯火阑珊   2019-02-05 15:38    回复了 sandychn 的评论         踩      回复

WA了?我记得我是AC的,算了不用管这个BUG,已经修改代码了.

用户头像
sandychn   2019-02-05 15:47    回复了 秦淮岸灯火阑珊 的评论         踩      回复

好嘞


用户头像
Hz   2019-01-29 15:20         踩      回复

哪里看到李煜东老师代码的?

用户头像
秦淮岸灯火阑珊   2019-01-29 16:26         踩      回复

书上有光盘,光盘上有的啊.

用户头像
Hz   2019-01-29 19:22    回复了 秦淮岸灯火阑珊 的评论         踩      回复

谢谢,我去看看

用户头像
秦淮岸灯火阑珊   2019-01-29 19:28    回复了 Hz 的评论         踩      回复

不谢

用户头像
尾生   2019-02-01 22:12         踩      回复

没有光盘的可以去GitHub上找,搜书名字就行了。


App 内打开
你确定删除吗?
1024
x

© 2018-2025 AcWing 版权所有  |  京ICP备2021015969号-2
用户协议  |  隐私政策  |  常见问题  |  联系我们
AcWing
请输入登录信息
更多登录方式: 微信图标 qq图标 qq图标
请输入绑定的邮箱地址
请输入注册信息