2025.2

2025.2

1. CF1091F - New Year and the Mallard Expedition

https://www.luogu.com.cn/problem/CF1091F

显然答案为所有 ba1,cb1 的 sg 异或和。问题变为怎样求 sg。

发现 sg 式子十分公式,形如 sgi=xor{sgiaj},这种式子在值域较小时可以使用 bitset 优化。设 pi,j 表示是否存在一个 sgk=j 使得 k 能贡献到 i,那么这个时候就简单了:

  • 更新,则令 p[sg_k] |= k << ok;
  • 查询,只用查询所有 p 的第 i 位即可。

复杂度 O(nV+n2w),时间复杂度 O(nVw)

点击查看代码
// Problem: H. New Year and the Tricolore Recreation
// Contest: Codeforces - Good Bye 2018
// URL: https://codeforces.com/problemset/problem/1091/H
// Memory Limit: 256 MB
// Time Limit: 4000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
int mx;
int n, b, f[N];
int p[N], v[N], pc, isp[N];
bitset<N> ok, sg[70];
int main(){
mx = N - 10;
for(int i = 2; i <= mx; ++ i){
if(!v[i]){
p[++pc] = i;
isp[i] = 1;
ok.set(i);
}
for(int j = 1; j <= pc && i * p[j] <= mx; ++ j){
v[i*p[j]] = 1;
if(i % p[j] == 0){
break;
}
}
}
for(int i = 1; i <= pc; ++ i){
for(int j = 1; j <= pc && p[i] * p[j] <= mx; ++ j){
ok.set(p[i]*p[j]);
}
}
scanf("%d%d", &n, &b);
ok.set(b, 0);
for(int i = 0; i <= mx; ++ i){
int val = 0;
for(int j = 0; j < 70; ++ j){
if(!sg[j].test(i)){
val = j;
break;
}
}
f[i] = val;
sg[val] |= ok << i;
}
int ans = 0;
while(n--){
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
ans ^= f[b-a-1] ^ f[c-b-1];
}
puts(ans ? "Alice\nBob" : "Bob\nAlice");
return 0;
}

2. qoj4228 - Double Sort

https://qoj.ac/contest/943/problem/4228

不考虑这个输出小数。来点 O(n2+mlogm) 做法。

原题意可以转化为:对于所有 bm 的正整数序列,对于每一个 i 求从小到大排序后第 i 项在所有方案中的和。

bbni+1=bk=1m[bni+1k]=k=1mf(i,k)

其中 f(i,k) 表示至少 i 个数 k 的方案数。

如何计算这个 f?考虑设 g(i,k) 表示恰好 i 个数 k 的方案数;h(i,k) 表示选定 1i 下标上的数 k 的方案数。

根据定义有:f(i,k)=j=ing(j,k)

根据二项式反演有:g(j,k)=p=jn(np)h(p,k)(pj)(1)pj

根据插版法有:h(p,k)=x=1m(xp(k1)1n1)=(xp(k1)n)

f(i,k)

f(i,k)=j=ing(j,k)=j=inp=jnh(p,k)(pj)(1)pj=p=in(np)h(p,k)(1)pij=ip(pj)(1)ji=p=in(np)h(p,k)(1)pi(p1i1)

最后一步可以在杨惠三角上直接证明。

带入,得:

bbni+1=k=1mf(i,k)=p=in(np)(1)pi(p1i1)k=1mh(p,k)

由于 h(p,k)0 的必要条件是 pkm,所以只用算 O(mlogm) 次。总复杂度 O(n2+mlogm)

3. LuoguP4707 - 重返现世

https://www.luogu.com.cn/problem/P4707

一直想写这个题,今天写一下,发现写起来很简单。

首先要知道扩展 min-max 容斥式子:

kthmax(S)=TS,T(|T|1k1)(1)|T|kmin(T)

题面求的是第 nk+1 大的期望(下文中的 knk+1)。那么可以转化为求若干最小期望。这个是好求的,集合 S 内的最小期望为 miSpi

容易列出 dp:设 fi,j 表示集合中有 i 个数,p 和为 j 的方案数,复杂度 O(n2m)

考虑利用上 k 很小(即原题面中的 |nk|10),考虑将这个组合数利用组合意义拆开来:即 i 个数中选定 k 个数,要求第一个数必选。

那么新的 dp 状态就是:设 fi,j 表示和为 i,且已经选了 j 个数,方案数乘上 1 的选的数的个数次方。对于每个 pp 在集合中的转移有两条(按照背包 dp 的方式转移,p 不在集合中的不用考虑):

  • fi,jfip,j,表示 p 放入,但是不选;要求 ip0(即不是第一个数);
  • fi,jfip,j1,表示 p 放入且选;要求 j>0(显然)。

初值 f0,0=(1)k,答案为 mifi,k

点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll P = 998244353;
int f[10010][13];
int n, k, m, p[1010];
ll qp(ll x, ll y){
ll ans = 1;
while(y){
if(y & 1){
ans = ans * x % P;
}
x = x * x % P;
y >>= 1;
}
return ans;
}
int main(){
f[0][0] = 1;
scanf("%d%d%d", &n, &k, &m);
k = n - k + 1;
for(int i = 1; i <= n; ++ i){
scanf("%d", &p[i]);
for(int v = m; v >= p[i]; -- v){
for(int c = 0; c <= k; ++ c){
if(c){
f[v][c] -= f[v-p[i]][c-1];
if(f[v][c] < 0){
f[v][c] += P;
}
}
if(v > p[i]){
f[v][c] -= f[v-p[i]][c];
if(f[v][c] < 0){
f[v][c] += P;
}
}
}
}
}
ll ans = 0;
for(int i = 1; i <= m; ++ i){
ans += qp(i, P-2) % P * f[i][k] % P;
ans %= P;
}
printf("%lld\n", ans * qp(P-1, k) % P * m % P);
return 0;
}
posted @   KiharaTouma  阅读(10)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起