Codeforces 963C(Math,Number Theory)

结题思路如下:

记一个长方向水平方向宽度为h,竖直方向宽度为w,对n类长方形先按h排序,再按w排序,并将所有h相同的长方形放到一个容器中。可知,h不同的每个容器,代表着所拼接成的大长方形的一列或者是几列。

对每个容器,如果一种w出现在一个容器中,则必然出现在其他所有容器中,且该中w的数目与该容器所有长方形数目的比值,与其他容器相应的比值相同。即任意一个容器的一类长度为w的长方形在其容器所在列所占行数与其它容器相同。

对每个容器求其所有w类型所占个数的gcd,该值记为gcdi,那么只考虑这个容器,能拼接出最少的行数为 min_rowi = sumi / gcdi. 然后对所有的min_rowi求最小公倍数得到min_row. 若对所有的sumi,有sumi % min_row == 0 ,那么此时min_row 就是能够考虑所有容器下的能拼出的最小行数。

因为sum i / min_rowi = gcdi ,若 再有 sum % min_row == 0 则有 sum / min_row 为 gcdi的因子,从而该容器可以拼出min_row 行 .

对于所有sumi % min_row == 0 只需要所有的 sumi的gcd(记为max_row) % min_row == 0 就可以了.

最后的枚举min_row 到  max_row之间所有的可行列方式需要注意技巧,只需做sqrt(max_row)次枚举即可。只要比sqrt(max_row)大的 max_row的因子X,必然可以在美剧 max_row / x 时,同时处理。

排序注意 时注意  return  x  != rhs.x ? x < rhs.x : y < rhs.y。(不等号)

 

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

#define mp make_pair

typedef long long ll;
typedef long double ld;

const int maxn = 205000;

vector<pair<ll,ll>> st[maxn];
int m = 0 , n ;
ll cnt[maxn];

struct node{
    ll h,w,num;
    bool operator<(const node& rhs)const{
        return h != rhs.h ? h < rhs.h : w < rhs.w;
    }
}a[maxn];
ll least_common_mutiple(ll x , ll y){
     ll temp = __gcd(x , y);
     return x / temp * y;
}
ll min_row =1;
ll max_row;
void read(){
   cin>>n;
   ll preh;
   for(int i=0;i<n;i++)
     scanf("%I64d %I64d %I64d",&a[i].h,&a[i].w,&a[i].num);
   sort(a,a+n);
   for(int i=0;i<n;i++) {
     if(i==0 || preh != a[i].h){
           preh = a[i].h; ++m;
     }
     st[m].push_back(mp(a[i].w,a[i].num)),cnt[m]+=a[i].num;
   }
   max_row = cnt[1];
   for(int i=2;i<=m;i++)
       max_row = __gcd(max_row , cnt[i]);
}
bool judge(){
    for(int i=1;i<=m;i++){
        if(st[i].size() != st[1].size()) return false;
    }
    for(int i=1;i<=m;i++){
        for(int j=0;j<st[i].size();j++){
             if(st[i][j].first != st[1][j].first) return false;
             if((ld)st[i][j].second / (ld) cnt[i] != (ld)st[1][j].second / (ld) cnt[1]) return false;
        }
    }
    for(int i=1;i<=m;i++){
        ll tt = st[i][0].second;
        for(int j=0;j<st[i].size();j++){
             tt = __gcd(st[i][j].second , tt);
        }
        tt = cnt[i] / tt;
        min_row = least_common_mutiple(min_row , tt);
    }
    if(max_row >= min_row && max_row % min_row == 0) return true;
    else return false;
}
void cal(){
   int ans = 0;
   for(ll i =1 ; i*i <=max_row ; i++){
         if( i % min_row == 0  && max_row  % i == 0)
                ans++;
         if(i * i != max_row && max_row % i == 0) {
               ll ti = max_row / i;
               if(ti % min_row == 0 && max_row % ti == 0)
                  ans++;
         }
   }
   cout<<ans<<endl;
}
int main()
{
    read();
    if(judge()) cal();
    else {
        cout<<"0";
    }
    return 0;
}
/*
4
4 1 1
1 1 1
4  3 1
1 3 1
*/

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值