ZROI 2022暑假AB班 Round6 解题报告
30 + 0 + 0,rk 38,暴力写挂。
A
首先我们考虑证明一个东西,即原问题有解当且仅当
证明:
我们考虑构造一个操作序列来满足上述条件,设
x< y 。如果我们把
x 倒k 桶进y 使得溢出,那么我们此时得到的就是kx\bmod y ,所以我们这里就可以构造出任何一个[0,y-1] 水量的桶,所以构造[x,x+y-1] 同样是简单的。
所以我们考虑就是要统计满足下列两个条件的
\gcd(a_x,a_y) | k a_x + a_y \geq k
我们考虑每个点只会在因数的地方有贡献,所以贡献是
对于
我们考虑对
所以我们可以强制某一个质因子不满足,对于一个
这类题启发我们对值域进行莫比乌斯反演,这个套路就是比如求
const int N = 1e6 + 10;
int n,k,a[N],maxx;
ll ans;
ll mu[N], v[N],pri[N],tot,f[N];
ll x, cnt[N];
bool vis[N];
vector <int> tmp;
void init(int m) {
mu[1] = 1;
for (int i = 2;i <= m;++i) {
if (!vis[i]) pri[++tot] = i,mu[i] = -1;
for (int j = 1;j <= tot && pri[j] * i <= m;++j) {
vis[pri[j] * i] = 1;
if (i % pri[j] == 0) {
break;
}
mu[i * pri[j]] = -mu[i];
}
}
}
bool cmp(int x,int y) {
return x > y;
}
signed main() {
//FO(p)
read(n,k);
for (int i = 1;i <= n;++i) read(a[i]),maxx = max(a[i],maxx),++cnt[a[i]];
init(maxx);
for (int i = 1;i <= maxx;++i) {
int now = (maxx / i) * i + i,tmp = 0;
//now = max(a_i) the most close | i
for (int j = i;j <= maxx;j += i) {
while (now && now + j >= k + i) {
now -= i;
tmp += cnt[now];
}
f[i] += tmp * cnt[j];
if (now <= j) f[i] -= cnt[j];
}
f[i] /= 2;//chongfu pairs.
}
for (int i = 1;i <= k;++i) {
if (k % i) continue;
ll now = f[i];
for (int j = i * 2;j <= maxx;j += i) {
now += f[j] * mu[j / i];
}
ans += now;
}
printf("%lld\n",ans);
return 0;
}
B
笛卡尔树做法
我们考虑这个东西可以很显然转化为笛卡尔树上一个极长的
实现没写,参考这个老哥做法:提交记录 #493594 - Zhengrui Online Judge (zhengruioi.com)
分治做法
我们直观考虑对
如果我们枚举这个
那么要满足:
a_l \geq b_r A_l + A_r = X - a_l
那么假设枚举了
所以我们考虑这个
分析一下复杂度,我们就是需要一个单点加,区间查询的数据结构,可以用数组实现(记得撤销保证复杂度)。右边的问题是对称的,一层分治的时间复杂度可以做到
const int N = 1e6 + 10;
int n,x,a[N],l[N],r[N];
ll ans;
ll buc[N];
void solve(int L,int R) {
if (L == R) {
if (a[L] * 3 == x) {
++ans;
}
return ;
}
int mid = (L + R) >> 1;
ll maxx = 0,cnt = 0;
int l,r;
for (l = mid,r = mid + 1;l >= L;--l) {
if (a[l] > maxx) maxx = a[l],cnt = 1;
else if (a[l] == maxx) ++cnt;
while (r <= R && a[r] <= maxx) buc[a[r]]++,++r;
if (x >= a[l] + maxx) ans += cnt * buc[x - a[l] - maxx];
}
for (int i = mid + 1;i <= R;++i) buc[a[i]] = 0;
maxx = cnt = 0;
for (l = mid,r = mid + 1;r <= R;++r) {
if (a[r] > maxx) maxx = a[r],cnt = 1;
else if (a[r] == maxx) ++cnt;
while (l >= L && a[l] <= maxx) buc[a[l]]++,--l;
if (x >= a[r] + maxx) ans += cnt * buc[x - a[r] - maxx];
}
for (int i = L;i <= mid;++i) buc[a[i]] = 0;
solve(L,mid);solve(mid + 1,R);
}
signed main() {
read(n,x);
for (int i = 1;i <= n;++i) read(a[i]);
solve(1,n);
printf("%lld\n",ans);
return 0;
}
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
留名%%%