【CF516E】Drazil and His Happy Friends(思维)

点此看题面

  • (n)个男生和(m)个女生(从(0)开始编号),一开始已知有(b)个男生和(g)个女生是快乐的。
  • (i)天编号为(i\%n)的男生和(i\%m)的女生会一起玩,如果有一个人是快乐的则另一个人也会变快乐。
  • 问至少多少天所有人都快乐。
  • (n,mle10^9,b,gle10^5)

问题的基础分解

至少这一步我还是能自己想到的。。。

考虑我们求出(d=gcd(n,m)),显然模(d)不同余的男女生之间永远不可能相互影响。

所以我们可以把原本的男女生按编号模(d)的余数分解成(d)个子问题。

显然一个子问题有解的充要条件是存在至少一个一开始是快乐的男生或女生。

那么当(d>b+g)的时候整个问题不可能有解,故(d)的大小得到了限制,控制在了(b+g)范围内。

独立思考男女生

根据题目给出的条件,显然如果编号为(i\%m)的女生在第(i)个时刻结束后是快乐的,那么在第(i+m)个时刻第((i+m)\%n)个男生就会变快乐。

既然第(i)个时刻结束后编号为(i\%m)的女生是快乐的,那么编号为(i\%n)的男生也必然是快乐的。

因此上面的结论就等价于如果编号为(i\%n)的男生在第(i)个时刻结束后是快乐的,那么在第(i+m)个时刻第((i+m)\%n)个男生就会变快乐

然后我们发现每个男生追根溯源都是因为某个一开始就快乐的男生或女生变快乐的。

则我们先直接处理掉一开始快乐的女生,让对应编号的男生变快乐,然后女生怎样不会对男生造成任何影响。(注意一开始快乐的女生对应编号的男生在对应编号的时刻才会快乐,对答案有贡献)

接下来考虑给男生重新编号,让原本编号为(km\%n)的男生的编号变为(k)

那么就变成了当一个男生快乐之后,下一个男生就会变快乐,所有男生形成一个大环。

则对于两个相邻的初始快乐的男生(x,y)之间的所有男生,显然都是因男生(x)变快乐的,且共需((y-x-1) imes m)个时刻实现这一转变。

那么对于一个子问题中男生的情况,只要枚举所有初始快乐的男生求一下最大时刻就好了。

整个问题就是对于(d)个子问题分别处理男女生两种情况就做完了。

代码:(O((b+g)log(b+g)))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 200000
#define LL long long
using namespace std;
int n,m,ta,tb,a[N+5],b[N+5];vector<int> A[N+5],B[N+5];
I void Gmax(LL& x,Con LL& y) {x<y&&(x=y);}I int gcd(CI x,CI y) {return y?gcd(y,x%y):x;}
I void exgcd(CI x,CI y,int& a,int& b) {y?(exgcd(y,x%y,b,a),b-=x/y*a):(a=1,b=0);}//exgcd
I int GetInv(CI x,CI y) {RI a,b;return exgcd(x,y,a,b),(a%y+y)%y;}//exgcd求逆元
set<int> S;I LL Work(CI n,CI m,vector<int> A,vector<int> B)//处理男生/女生的情况
{
	if(n==1) {if(A.size()) return -1;if(B.size()) return B[0];puts("-1"),exit(0);}//特判只有1人,注意-1是为了不影响答案而非无解
	RI t,Inv=GetInv(m,n);LL s=-1;vector<int>::iterator i;S.clear();//Inv记录1/m,注意乘Inv表示旧变成新,乘m表示新回到旧
	for(i=A.begin();i!=A.end();++i) S.insert(1LL**i*Inv%n);//一开始快乐的男生
	for(i=B.begin();i!=B.end();++i) S.find(t=1LL**i*Inv%n)==S.end()&&(S.insert(t),Gmax(s,*i),0);//因一开始快乐的女生快乐的男生,注意对答案贡献
	if(S.empty()) {puts("-1");exit(0);}set<int>::iterator x,y;
	for(x=S.begin(),y=++S.begin();y!=S.end();++x,++y) (t=*y-*x-1)&&(Gmax(s,1LL*t*m+1LL**x*m%n),0);//枚举两个相邻的初始快乐的男生
	return y=S.begin(),(t=n-1-*x+*y)&&(Gmax(s,1LL*t*m+1LL**x*m%n),0),s;//特殊处理首尾二位
}
int main()
{
	RI i;for(scanf("%d%d%d",&n,&m,&ta),i=1;i<=ta;++i) scanf("%d",a+i);
	for(scanf("%d",&tb),i=1;i<=tb;++i) scanf("%d",b+i);
	RI d=gcd(n,m);if(d>ta+tb) return puts("-1"),0;sort(a+1,a+ta+1),sort(b+1,b+tb+1);
	for(i=1;i<=ta;++i) A[a[i]%d].push_back(a[i]/d);for(i=1;i<=tb;++i) B[b[i]%d].push_back(b[i]/d);//分成d个子问题
	LL ans=0;for(i=0;i^d;++i) Gmax(ans,max(Work(n/d,m/d,A[i],B[i]),Work(m/d,n/d,B[i],A[i]))*d+i);//注意每个子问题的答案还需稍加修正
	return printf("%lld
",ans),0;
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF516E.html