[CF1500B]Two chandeliers

280 篇文章 1 订阅

题目

传送门 to CF

思路

i = k n + a i=kn+a i=kn+a 天,考虑两个灯是否相同,就可以检查二分值。即
∑ k n + a ≤ t [ p a ≠ q ( k n + a )   m o d   m ] \sum_{kn+a\le t}[p_a\ne q_{(kn+a)\bmod m}] kn+at[pa=q(kn+a)modm]

k n   m o d   m = x 0 kn\bmod m=x_0 knmodm=x0,实际上我们就要求
∑ a [ p a ≠ q ( a + x 0 )   m o d   m ] \sum_{a}[p_a\ne q_{(a+x_0)\bmod m}] a[pa=q(a+x0)modm]

( k + 1 ) n ≤ t (k+1)n\le t (k+1)nt 时, a a a 就没有限制了。所以多数时候 a a a 是无限制的。如果对于所有 x 0 x_0 x0 都求出这个值,就不需要二分了,直接用 k k k 暴力增,最后确定 t − k n t-kn tkn 的值。所以这个无限制的问题怎么做?

正难则反,考虑统计有多少个是相等的。那么 p a = q b p_a=q_b pa=qb 会贡献到 x 0 = b − a x_0=b-a x0=ba 上面去。显然这就是卷积——将 p p p 翻转即可。

问题是,下标的范围挺大的,而权值太多,就会出问题。考虑经典 “大小点”,设定阙值 M M M,当前权值数量不超过 M M M 就平方硬算,否则 m log ⁡ m m\log m mlogm 卷积。显然复杂度是
O ( m M + m 2 log ⁡ m M ) ≥ O ( m m log ⁡ m ) \mathcal O\left(mM+\frac{m^2\log m}{M}\right)\ge\mathcal O(m\sqrt{m\log m}) O(mM+Mm2logm)O(mmlogm )

发现会 T L E TLE TLE,然后我就想不出来了。考试结束后, H a n d I n D e v i l \sf HandInDevil HandInDevil 突然告诉我, p , q p,q p,q 中数字互不相同,所以每种权值都是最多出现 2 2 2 次……

所以就做到 O ( n + m ) \mathcal O(n+m) O(n+m) 了。这不读题的毛病,啥时候能改改!

代码

#include <bits/stdc++.h>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
typedef long long int_;
inline int_ readint(){
	int_ a = 0; char c = getchar(), f = 1;
	for(; c<'0'||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
inline void writeint(int_ x){
	if(x < 0) putchar('-'), x = -x;
	if(x > 9) writeint(x/10);
	putchar((x-x/10*10)^48);
}

const int MaxN = 1000005;
int a[MaxN], b[MaxN], cnt[MaxN];
int A[MaxN], B[MaxN];

int main(){
	int n = readint(), m = readint();
	int_ k = readint();
	int d = __gcd(n,m);
	rep(i,1,n) a[A[i] = readint()] = i;
	rep(i,1,m) b[B[i] = readint()] = i;
	if(n > m){
		swap(n,m), swap(a,b);
		swap(A,B); // for convenience
	}
	rep(i,1,m<<1) if(a[i] && b[i])
		++ cnt[(b[i]+m-a[i])%m];
	int_ peri = 0; rep(i,0,m/d-1)
		peri += n-cnt[1ll*i*n%m];
	int_ ans = 1ll*n*m/d*((k-1)/peri);
	k = (k-1)%peri+1; // period
	rep(i,0,m/d-1)
		if(k <= n-cnt[1ll*i*n%m]){
			int t = 1ll*i*n%m;
			for(int j=1; j<=n; ++j,++ans)
				if(A[j] != B[(j+t-1)%m+1])
				if((-- k) == 0) break;
			++ ans; break;
		}
		else{
			k -= (n-cnt[1ll*i*n%m]);
			ans += n; // n more days
		}
	printf("%lld\n",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值