习题:七夕祭(杂题)

题目

思路

首先一点如果是impossible,那么一定

\(T\%N\)和\(T\%M\)都不为0

再接着,

因为竖着满足跟横着满足本质上是一样的,所以这里只讨论横着满足

并且如果要满足横着的情况,

如果要步数最小,那么我们一定不会将竖着的摊位交换

同理,如果要满足竖着的情况,我们一定不会将横着的摊位交换

也就是说如果我们定义两个数组row和col来维护每一行和每一列的摊位个数

那么竖着交换对col数组没有任何影响,横着交换对row数组也没有任何影响

也就是指,如果是both的话,

我们只需要将横着交换的最小值和竖着交换的最小值相加即可

因为我们只需要横着满足,所以我们其实并不在意整个矩阵长成什么样子

我们所需要知道的,只是每一横着有多少个摊点

问题转换为:

现在我们有一个环,我们只能使相邻的元素一个+1,一个-1,求最小的步数使这个数列的元素变为一样

然后?

这个模型你们不熟悉?

如果真不熟悉的话,

蒟蒻笔者还是解释一下

t就是题意中的t

我们先对于每一个元素求出他到指定元素的差距,即\(row_i=row_i-t/n\)

接着求出前缀和数组\(srow\),然后求出对\(srow\)排序之后的中点\(mid=srow_{(n+1)/2}\),n+1是防止精度

最后\(ans=\sum_{i=1}^{n}abs(srow_i-mid)\)

那么问题来了,原理是什么?

考虑一下,\(srow_i\)表示什么?

其实就是1~i之中总共需要对外部所提供的贡献,或者他需要外界所需的贡献

我们定义对外部的贡献为正,所需的贡献为负

现在讨论的\(srow\)是指没有排序的\(srow\)

我们先指定一个点k

\(srow_k-srow_1\)的意义是什么?

不就是2~k这个区间为了使1到达平均元素时候的贡献

那么\(srow_k-srow_2\)的意义是什么?

同理,就是3~k这个区间为了使1~2这个区间的和达到平均时所作的贡献

那么\(srow_k-srow_1+srow_k-srow_2\),意义是什么?

不就是先使1达到平均,再使1~2这个区间的和达到平均的贡献和

因为1我们已经付出代价将其达到平均,所以2也一定是平均的,

所以对于一个指定的k

\(ans=\sum_{i=1}^{n}abs(srow_i-srow_k)\)

很明显,当k为中位数的时候最小

代码


#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
struct node
{
    int x,y;
}a[200005];
int n,m,t;
int col[200005];
int row[200005];
int s_row[200005];
int s_col[200005];
int ans_row;
int ans_col;
int f_abs(int x)
{
    if(x<0)
        return -x;
    return x;
}
signed main()
{
    cin>>n>>m>>t;
    if(t%n!=0&&t%m!=0)
    {
        cout<<"impossible";
        return 0;
    }
    for(int i=1;i<=t;i++)
    {
        cin>>a[i].x>>a[i].y;
        row[a[i].x]++;
        col[a[i].y]++;
    }
    if(t%n==0)
    {
        for(int i=1;i<=n;i++)
            row[i]=row[i]-t/n;
        for(int i=1;i<=n;i++)
            s_row[i]=s_row[i-1]+row[i];
        sort(s_row+1,s_row+1+n);
        int mid=s_row[(n+1)/2];
        for(int i=1;i<=n;i++)
            ans_row+=f_abs(mid-s_row[i]);
    }
    if(t%m==0)
    {
        for(int i=1;i<=m;i++)
            col[i]=col[i]-t/m;
        for(int i=1;i<=m;i++)
            s_col[i]=s_col[i-1]+col[i];
        sort(s_col+1,s_col+1+m);
        int mid=s_col[(m+1)/2];
        for(int i=1;i<=m;i++)
            ans_col+=f_abs(mid-s_col[i]);
    }
    if(t%n==0&&t%m==0)
    {
        cout<<"both "<<ans_row+ans_col;
        return 0;
    }
    if(t%n==0)
    {
        cout<<"row "<<ans_row;
        return 0;
    }
    if(t%m==0)
    {
        cout<<"column "<<ans_col;
        return 0;
    }
    return 0;
}

原文地址:https://www.cnblogs.com/loney-s/p/12179459.html

时间: 2024-07-31 06:05:34

习题:七夕祭(杂题)的相关文章

[Poetize II]七夕祭

描述 Description TYVJ七夕祭和11区的夏祭的形式很像.矩 形的祭典会场由N排M列共计N×M个摊点组成.虽然摊点种类繁多,不过cl只对其中的一部分摊点感兴趣,比如章鱼烧.苹果糖.棉花糖.射的屋……什么的. Vani预先联系了七夕祭的负责人zhq,希望能够通过恰当地布置会场,使得各行中cl感兴趣的摊点数一样多,并且各列中cl感兴趣的摊点数也一样多.    不 过zhq告诉Vani,摊点已经随意布置完毕了,如果想满足cl的要求,唯一的调整方式就是交换两个相邻的摊点.两个摊点相邻,当且仅

bzoj3032 七夕祭题解

题面 TYVJ七夕祭和11区的夏祭的形式很像.矩形的祭典会场由N排M列共计N×M个摊点组成.虽然摊点种类繁多,不过cl只对其中的一部分摊点感兴趣,比如章鱼烧.苹果糖.棉花糖.射的屋--什么的.Vani预先联系了七夕祭的负责人zhq,希望能够通过恰当地布置会场,使得各行中cl感兴趣的摊点数一样多,并且各列中cl感兴趣的摊点数也一样多. 不过zhq告诉Vani,摊点已经随意布置完毕了,如果想满足cl的要求,唯一的调整方式就是交换两个相邻的摊点.两个摊点相邻,当且仅当他们处在同一行或者同一列的相邻位置

【最小生成树杂题】

这里谈一下最小生成树 生成树的概念:连通图G的一个子图如果是一棵包含G的所有顶点的树,则该子图称为G的生成树.生成树是连通图的极小连通子图.所谓极小是指:若在树中任意增加一条边,则将出现一个回路:若去掉一条边,将会使之变成非连通图. 生成树各边的权值总和称为生成树的权.权最小的生成树称为最小生成树. 最小生成树一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边.常用于求最小生成树得算法包括kruskal(克鲁斯卡尔)算法或Prim(

_杂题_

杂题集 是个放题的好地方! **** 5.28 **** - BZOJ [3052] 糖果公园 - 据说是一道区间操作的综合题,但现在貌似蹦了? 现在还是太水,之后再来写吧. *************

[杂题]URAL1822. Hugo II&#39;s War

看懂题意的请直接跳过下一坨! 本人有表达障碍! ========================================== 题意: (题意真的很难很难懂啊!!!  去他娘的**) 有一个王国,王国里有一个国王(编号为1),他有(编号为2~n) n-1个臣子(这些臣子并不全和他有直接关系) 然后呢 国王要去打架,但是只有当他的x%个及以上的直系下属(与他有直接关系的臣子)做好打架的准备了,他才能去打架 他的直系下属也有下属,也要其中x%及以上的下属做好打架准备了,那些直系下属才会开始准备

hdu 3641 数论 二分求符合条件的最小值数学杂题

http://acm.hdu.edu.cn/showproblem.php?pid=3641 学到: 1.二分求符合条件的最小值 /*==================================================== 二分查找符合条件的最小值 ======================================================*/ ll solve() { __int64 low = 0, high = INF, mid ; while(low <=

hdu 4961 数学杂题

http://acm.hdu.edu.cn/showproblem.php?pid=4961 先贴个O(nsqrtn)求1-n所有数的所有约数的代码: vector<int>divs[MAXN]; void caldivs() { for(int i=1;i<MAXN;i++) for(int j=i;j<MAXN;j+=i) divs[j].push_back(i); } 有了这个当时理下思路就可写了,但是重复数处理注意: 1.用一个数组vis[]  vis[i]=1表示i存在

poj 杂题 - 1959 Darts

这一题放在杂题里,是因为我没有用DP,而是使用的枚举,当然是受到了discuss里面的启发. 因为我们只能有三次机会,每一次只可以是固定的63个数,所以枚举感觉更加直观,但是不知道是不是没有DP快. #include<stdio.h> #include<string.h> int n; int Darts[63]; int main(){ int t,c=1,i,j,k,res; scanf("%d",&t); for(i = 0 ;i<=20;i

七夕专场-D题

一个map的超水题. #include <cstdio> #include <iostream> #include <cstring> #include <queue> #include <algorithm> #include <algorithm> #define LL long long using namespace std; const int maxn = 200010; const int maxm = 200100;

七夕专场-F题

LH很聪明.每次这种题目,他想想就有结果了,我得琢磨一阵才恍然大悟.诶,智商不在一个等级啊.其实是用做差,一直用新读入的减去前面2个数之差.sum为0就满足. #include<iostream> using namespace std; int g[10010]; int main() { int t,m; cin >> t; while(t--) { cin >> m; int sum = 0; for(int i = 0;i <m;i++) { cin &g