https://codeforces.com/contest/1501
C. Going Home
-
题意:给定一个长度为n的序列,找到一组 x , y , z , w x,y,z,w x,y,z,w,使 a x + a y = a y + a w a_x+a_y=a_y+a_w ax+ay=ay+aw
-
思路:注意数据范围
4 ≤ n ≤ 2 e 5 4≤n≤2e5 4≤n≤2e5
1 ≤ a i ≤ 2.5 e 6 1≤a_i≤2.5e6 1≤ai≤2.5e6
因为两个数的和不会超过5e6,所以我们可以枚举所有数对,存下每一个和值所对应的两个数的下标,当某个和值中下标个数大于等于4个,就有解了。(因为每个数只能使用一次,所以只有当两个数下标都没有出现过时,才能存进去) -
复杂度分析:不考虑下标重复利用的问题,那么当有4个数对的和相同时,就一定有至少一组解,所以在整体的复杂度不会超过O(3*5e6+1)。(抽屉原理)
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<set>
using namespace std;
#define pii pair<int,int>
const int manx = 5e6 + 10;
set<int>fre[manx];
int a[manx/10];
int main()
{
int n,ans=-1;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
int temp=a[i]+a[j];
if(!fre[temp].count(i) && !fre[temp].count(j)){
fre[temp].insert(i);
fre[temp].insert(j);
}
if((int)fre[temp].size()>=4){
ans=temp;
break;
}
}
if(ans!=-1)break;
}
if(ans==-1){
puts("NO");
return 0;
}
puts("YES");
pii x[4];
int cou=0;
for(auto it:fre[ans])x[cou].first=a[it],x[cou++].second=it;
sort(x,x+4);
printf("%d %d %d %d\n",x[0].second,x[3].second,x[1].second,x[2].second);
return 0;
}
D. Two chandeliers
- 题意:给定两个长度分别为n和m的序列,两个序列循环扩展,问两数组中不相同的第k个数下标为多少。
- 思路1:对于每一个数,设它在两个数组中相遇的位置为pos,则有:
p o s ≡ i ( m o d n ) pos≡i(mod\ n) pos≡i(mod n)
p o s ≡ j ( m o d m ) pos≡j(mod\ m) pos≡j(mod m)
这里就可以用excrt(扩展中国剩余定理)得到pos的最小整数解 p o s ’ pos’ pos’
另外可以注意到,如果pos有解,那么在n和m的一个lcm长度内,存在有且仅有一个解,所以我们就可以算出一个lcm长度内有多少个下标对应的数值是相同的。
剩下的就可以二分答案了,因为有前面的结论,我们可以直接算出当前二分的数值中每一个lcm所含数值不同的下标个数,不满lcm长度的也可以二分得到数值不同的下标个数。 - 思路2:因为上面的同余方程只有两个,所以也可以直接合并 i + k 1 ∗ n = j + k 2 ∗ m i+k_1*n=j+k_2*m i+k1∗n=j+k2∗m,然后用exgcd求解。
- 注意:数据范围: 1 ≤ a i , b j ≤ 2 ∗ m a x ( n , m ) 1≤a_i,b_j≤2*max(n,m) 1≤ai,bj≤2∗max(n,m)
- 方程ax+by = c最小非负整数解的处理
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<bitset>
using namespace std;
//#define int long long
#define ll long long
#define ull unsigned long long
#define pii pair<int,int>
#define mid ((l + r)>>1)
#define chl (root<<1)
#define chr (root<<1|1)
#define lowbit(x) ( x&(-x) )
const int manx = 1e6 + 10;
const int manx2 = 4e7 + 10;
const int INF = 1e9;
const int mod = 244353;
ll n,m,p,gcd;
ll posa[manx],posb[manx],meet[manx];
ll k,cnt=0;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(b==0){
x=1,y=0;
return a;
}
ll gcd=exgcd(b,a%b,y,x);
y-=(a/b)*x;
return gcd;
}
ll query(ll x)
{
ll ans=x/(n*m/gcd)*cnt;
ans+=upper_bound(meet+1,meet+cnt+1,x%(n*m/gcd))-meet-1;
return x-ans;//先算相同的,最后减
}
int main()
{
memset(posa,0,sizeof posa);
memset(posb,0,sizeof posb);
scanf("%lld%lld%lld",&n,&m,&k);
ll x1,y1;
gcd=exgcd(n,m,x1,y1);
for(int i=1;i<=n;i++){
scanf("%lld",&p);
posa[p]=i;
}
for(int i=1;i<=m;i++){
scanf("%lld",&p);
posb[p]=i;
}
for(int i=1;i<=2*max(n,m);i++){
ll x=x1,y=y1,c=posb[i]-posa[i];
if(!posa[i] || !posb[i] || c%gcd)//若数值a或b中没有这个数,或c不能整除gcd,则无解
continue;
x=x*c/gcd;
x=(x%(m/gcd)+m/gcd)%(m/gcd);
meet[++cnt]=posa[i]+n*x;
}//cnt就是每个lcm内相同数值的下标个数
sort(meet+1,meet+cnt+1);
ll l=0,r=2e18,ans;
while(l<=r){
ll temp=query(mid);
if(temp==k){
ans=mid;
r=mid-1;
}
else if(temp<k)l=mid+1;
else r=mid-1;
}
printf("%lld\n",ans);
return 0;
}