Codeforces1389 E. Calendar Ambiguity(数论)

题意:

给定m,d,w,
表示有m个月,每个月有d天,每周有w天

问有多少个数对(x,y),满足x<y,且第x个月的第y天与第y个月的第x天,星期相同

数据范围:m,d,w<=1e9

解法:
计算第x个月的第y天是周几:((x-1)d+y)%w+1

根据题目可以列出式子:(x-1)d+y=(y-1)d+x (%w)
转化一下得:(y-x)(d-1)%w=0

那么(y-x)(d-1)必须是w的倍数
因为(y-x)是变量,(d-1)和w是常量,所以(d-1)和w可以再约一下,
那么可以将(d-1)和w同时除以gcd(d-1,w),这样w更小,(y-x)的可选取范围更大
设g=gcd(d-1,w),那么(y-x)((d-1)/g)%(w/g)=0
现在(d-1)/g和w/g是互质的,要想令式子成立,只能让(y-x)(w/g)的倍数

要注意的点:
因为可能存在第x月第y天,但是不存在第y月第x天,
所以x和y不能超过min(n,d),令m=min(n,d)
则x,y<=m

令w1=w/gcd(d-1,w)
1.当y=m时:
因为y>x且x最小为1,那么y的范围为[2,m],(y-x)的范围为[1,m-1],贡献为(m-1)/w1
2.当y=m-1:
同上,(y-x)的范围为[1,m-2],贡献为(m-2)/w1
...
贡献最少的时候是w1/w1,因为如果y的取值再向下,贡献就是0了

最后答案肯定是k,k,.....2,2,2,1,1,1,的形式

发现有很多(m-kk)/w1是相同的,例如w1/w1与(2*w1-1)/w1是相同的,
[w1,2*w1-1]区间长度为w1,也就是说有若干段长度为w1的贡献相同,
显然只有数值最大的最后一段(上面的k,k,..那段)可能不为w1个,前面每一段都是w1个

最后一段的个数为m%w1
为啥是m不是(m-1)?
例如:
0,1,23等于0,
3,4,53等于1,
...
9,103等于3
虽然10%3等于1,而实际上[0,10]11个数,所以应该是11%3,最后答案为2
(可能这样麻烦了,但是我不知道怎么推导才比较正常)

如果m%w1=0,说明全部都是w1个
否则假设f=m%w1,最后一组是f个,其他都是w1个
总贡献用等差数列公式计算一下即可
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e5+5;
int n,d,w;
signed main(){
    int T;cin>>T;
    while(T--){
        cin>>n>>d>>w;
        w/=__gcd(d-1,w);
        int m=min(n,d);
        //
        int ans=0;
        int t=m%w;
        if(t==0){//全部都是w个
            int tt=(m-1)/w;
            ans+=tt*(tt+1)/2*w;
        }else{//最后一段t个,其他都是w个
            int tt=(m-1)/w;
            ans+=(tt-1)*tt/2*w;
            ans+=t*tt;
        }
        cout<<ans<<endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值