CodeForces 1389E Calendar Ambiguity 题解

CF1389E链接

首先你得看懂题目以及它给你的图。“‘第 \(x\) 个月的第 \(y\) 天’ 与 ‘第 \(y\) 个月的第 \(x\) 天’ 在它的那周的同一天“怎么理解?

”第 \(x\) 个月的第 \(y\) 天“ 转换为 “总共第 \((x-1)d + y\) 天”,“第 \(y\) 个月的第 \(x\) 天“ 转换为 “总共第 \((y-1)d + x\) 天”。

同时,可以发现“在它的那周 的同一天”时,两个天数是模 \(w\) 同余的。

也就是说,当 \(1 \leq x,y \leq min(m,d)\) 时,

\[(x-1)d + y \equiv (y-1)d + x \pmod w \]

注意到 \((x-1)d\) 实质上是 \(dx - d\)\((y-1)d\) 实质上是 \(dy - d\) ,约去 \(-d\) ,可得

\[dx + y \equiv dy + x \pmod w \]

移项,得

\[dx - x \equiv dy - y \pmod w \]

\[(d-1)x \equiv (d-1)y \pmod w \]

\(d-1\)\(w\) 可能不是互质的。考虑令它们互质,把它们除以 \(gcd(d-1,w)\),得到新的 \(D\)\(w'\)\(D = \frac{d-1}{gcd(d-1,w)}\),\(w' = \frac{w}{gcd(d-1,w)}\)),这样就可以保证 \(x < w'\) 时,\(Dx \mod w'\) 互不相同。这样就可以光明正大地消去 \(D\) 了。

\[x \equiv y \pmod {w'} \]

\(min(m,d)\)\(d\) 是原先的,不是大写) 为 \(lim\)。如果 \(lim\) 比较小,可以直接枚举余数 \(r\) ,计算出 \([1,lim]\) 中满足 \(k \mod w'\)\(k\) 的数量 \(num\),它的贡献为 \(\frac{num(num-1)}{2}\)。这里 \(lim\) 可能很大,换个做法,我们发现 \(num\) 的取值只有两种情况。

\(num = \lfloor \frac{lim}{w'} \rfloor\) ,有 \((w' - (lim \mod w'))\) 组;

\(num = \lfloor \frac{lim}{w'} \rfloor + 1\) ,有 \((lim \mod w')\) 组。

两种累加起来就好了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mit map<int,int>::iterator
#define sit set<int>::iterator
#define itrm(g,x) for(mit g=x.begin();g!=x.end();g++)
#define itrs(g,x) for(sit g=x.begin();g!=x.end();g++)
#define ltype int
#define rep(i,j,k) for(ltype(i)=(j);(i)<=(k);(i)++)
#define rap(i,j,k) for(ltype(i)=(j);(i)<(k);(i)++)
#define per(i,j,k) for(ltype(i)=(j);(i)>=(k);(i)--)
#define pii pair<int,int>
#define fi first
#define se second
#define mpr make_pair
#define pb push_back
#define fastio ios::sync_with_stdio(false)
const int inf=0x3f3f3f3f,mod=1000000007;
const double pi=3.1415926535897932,eps=1e-6;
void chmax(int &x,int y){if(x < y) x = y;}
void chmin(int &x,int y){if(x > y) x = y;}
int T,m,d,w;
int gcd(int x,int y){
    if(!y) return x;
    return gcd(y, x%y);
}
ll f(ll x){
    return x * (x-1) / 2;
}
int main()
{
    fastio;
    cin>>T;
    while(T--) {
        cin>>m>>d>>w;
        int k = gcd(d - 1, w);
        int w1 = w / k, lim = min(d, m);
        ll A = ll(lim % w1) * f(lim / w1 + 1), B = ll(w1 - lim % w1) * f(lim / w1);
        cout<<A+B<<'\n';
    }
    return 0;
}
posted @ 2020-07-31 17:00  beacon_cwk  阅读(220)  评论(0编辑  收藏  举报