2021.2 做题记录
Gym102896F Find a Square
题目描述:给定正整数 \(a,b,c,n\),设 \(f(k)=ak^2+bk+c\),求 \(\prod_{k=0}^{n-1}f(k)\) 的最大平方因子\(\bmod(10^9+7)\)。
数据范围:\(a,b,c,n\le 6\cdot 10^5\)。
solution
质因子 \(p^e\) 对答案的贡献是 \(p^{e-e\bmod 2}\),因此不需要考虑 \(e=1\) 的情况。
对于一个质数 \(p\),考虑如何求出其指数。当 \(p|2a\) 时直接暴力,这样的素数只有 \(\omega(2a)\) 个。
当 \(p\bot 2a\) 时,可以求出 \(ak^2+bk+c\equiv0\pmod p\) 的根,这样的根在模意义下至多只有两个,因此总共至多有 \(\lceil\frac{2n}p\rceil\) 个。
问题就转化为如何找到这些质数。若要判 \(m\) 个则时间复杂度为 \(O(m\log p)\)。
首先判掉 \(\le1222222\) 的所有质数,之后每个 \(f(k)\) 至多剩下两个质因子。只用考虑至少整除其中两个数的质数 \(p\),即存在 \(x,y\) 使得 \(p|\gcd(f(x),f(y))=\gcd(ax^2+bx+c,(x-y)(a(x+y)+b))\),得到存在 \(x<2n\) 使得 \(p|(ax+b)\),用同样的方法筛出这些质数即可。
最后剩下的可能是平方数,再判一遍就可以了。
#include<bits/stdc++.h>
#define PB emplace_back
#define MP make_pair
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef long double LD;
typedef __int128 LLL;
typedef pair<int, int> pii;
const int N = 1222222, mod = 1e9 + 7;
template<typename T>
void read(T &x){
int ch = getchar(); x = 0; bool f = false;
for(;ch < '0' || ch > '9';ch = getchar());
for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
if(f) x = -x;
}
template<typename T>
bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int a, b, c, n, tot, ans = 1; LL num[N], pri[N];
bool notp[N];
LL mul(LL a, LL b, LL p = mod){
LL d = (LL)((LD)a / p * b + 0.5);
LL r = a * b - p * d; return (r % p + p) % p;
}
LL ksm(LL a, LL b, LL p = mod){
LL res = 1;
for(;b;b >>= 1, a = mul(a, a, p))
if(b & 1) res = mul(res, a, p);
return res;
}
void init(int m){
notp[0] = notp[1] = true;
for(int i = 2;i <= m;++ i){
if(!notp[i]) pri[tot++] = i;
for(int j = 0;j < tot && i * pri[j] <= m;++ j){
notp[i * pri[j]] = true;
if(!(i % pri[j])) break;
}
}
} LL i2, pr;
struct comp {
LL x, y;
comp(LL _x = 0, LL _y = 0): x(_x), y(_y){}
comp operator * (const comp &o) const {
return comp((mul(x, o.x, pr) + mul(mul(y, o.y, pr), i2, pr)) % pr, (mul(x, o.y, pr) + mul(y, o.x, pr)) % pr);
} comp operator *= (const comp &o){*this = *this * o; return *this;}
};
LL Cipolla(LL x, LL p){
if(!x) return 0;
if(ksm(x, p-1>>1, p) != 1) return -1;
if((p & 3) == 3) return ksm(x, p+1>>2, p);
LL a = 1;
while(true){
i2 = (mul(a, a, p) + p - x) % p;
if(ksm(i2, p-1>>1, p) != 1) break; ++ a;
} comp tt(a, 1), res(1); pr = p;
for(LL b = p+1>>1;b;b >>= 1, tt *= tt) if(b & 1) res *= tt;
return res.x;
}
int main(){
read(a); read(b); read(c); read(n); init(N-1);
for(int k = 0;k < (n<<1);++ k) num[k] = (LL)a * k + b;
for(int i = 0;i < tot;++ i){
LL p = pri[i];
if(!(a % p)) for(int j = 0;j < (n<<1);++ j)
while(!(num[j] % p)) num[j] /= p;
else for(int j = (p - b % p) * ksm(a % p, p-2, p) % p;j < (n<<1);j += p)
while(!(num[j] % p)) num[j] /= p;
} for(int k = 0;k < (n<<1);++ k) if(num[k] > 1) pri[tot++] = num[k];
sort(pri, pri+tot); tot = unique(pri, pri+tot) - pri;
for(int k = 0;k < n;++ k) num[k] = ((LL)a * k + b) * k + c;
LL delta = (LL)b * b - 4ll * a * c;
for(int i = 0;i < tot;++ i){
LL p = pri[i], cnt = 0;
if(!((a<<1) % p)) for(int j = 0;j < n;++ j)
while(!(num[j] % p)){num[j] /= p; ++ cnt;}
else {
LL Sqr = Cipolla((delta % p + p) % p, p);
if(Sqr == -1) continue;
LL inv = ksm((a<<1) % p, p-2, p);
LL val = mul((Sqr + p - b % p) % p, inv, p);
for(LL j = val;j < n;j += p)
while(!(num[j] % p)){num[j] /= p; ++ cnt;}
val = mul((p - (Sqr + b) % p) % p, inv, p);
for(LL j = val;j < n;j += p)
while(!(num[j] % p)){num[j] /= p; ++ cnt;}
} ans = ans * ksm(p % mod, cnt - (cnt & 1)) % mod;
}
for(int k = 0;k < n;++ k){
LL Sqr = sqrt(num[k]);
for(LL i = max(Sqr-1, 0ll);i <= Sqr+1;++ i)
if(i * i == num[k]) ans = num[k] % mod * ans % mod;
} printf("%d\n", ans);
}
*CF1383F Special Edges
题目描述:给定 \(n\) 个点 \(m\) 条边的无向图,\(k\) 条是特殊边,非特殊边有给定容量。\(q\) 次查询给定特殊边的容量,求 \(1\rightarrow n\) 的最大流。
数据范围:\(n,m\le 10^4,k\le 10,q\le 2\cdot10^5,w\le 25\)。
solution
考虑转化成最小割,钦定每条特殊边是否被割,然后跑最大流。直接暴力是过不去的。要优化一下。
首先用 Dinic 在原图上跑出残余网络,然后钦定一条边没被割就是加一条边权为 \(+\infty\) 的边。
将 \(2^k\) 个残余网络存下来,维护加一条边跑网络流可以使用 FF 算法,即暴力找一条增广路然后增广。
时间复杂度 \(O((wm+q)2^k)\)。空间也要卡一卡...然后就懒得写了...
*CF1393E Twilight and Ancient Scroll
题目描述:给定 \(n\) 个字符串 \(s_i\),求对每个字符串,可以删除一个字符或保持原样,使得这些字符串的字典序单调不降的方案数。
数据范围:\(n\le 10^5,\sum |s_i|\le 10^6\)。
solution
对每个字符串的所有方案排序,很容易做到 \(O(\sum|s_i|\log|s_i|)\)。
然后直接 dp,时间复杂度 \(O(\sum|s_i|)\)。这就是 3200 么...
*CF1214G Feeling Good
题目描述:给定 \(n\times m\) 的 01 矩阵 \(M\),初始时全 0,每次操作给定 \(a,l,r\) 表示 \(\forall i\in[l,r]\) 将 \(M_{a,i}\) 反转,并求出一个左上角为 \((x_1,y_1)\),右下角为 \((x_2,y_2)\) 的子矩形,使得
- \(1\le x_1<x_2\le n,1\le y_1<y_2\le m\)。
- 矩阵的四个角,同侧不同,对角相同。
需判断无解。
数据范围:\(n,m\le 2000,q\le 5\cdot 10^5\)。
壬被玩傻了
solution
用 bitset 维护每一行,设第 \(i\) 行 \(1\) 的位置集合是 \(S_i\),只需找到两行 \(i,j\) 使得 \(S_i,S_j\) 互不包含即可。
小性质:按集合大小排序之后,\(\forall i<j<k\),\(S_i,S_k\) 互不包含 \(\Rightarrow\) \(S_i,S_j\) 互不包含或 \(S_j,S_k\) 互不包含。
于是用 set 维护 \(1\) 的个数的大小关系,用 bitset 判断两行间包含关系即可。
复杂度 \(O(\frac{m(n+q)}w+q\log n)\)(甚至还挺小...)
*CF1214H Tiles Placement
题目描述:给定 \(n\) 个点的树和正整数 \(k\),求给点染 \(k\) 种颜色的方案,使得任意一条点数为 \(k\) 的简单路径有 \(k\) 种颜色。需判断无解。
数据范围:\(2\le k\le n\le 2\cdot10^5\)。
solution
奇怪结论题...
首先特判 \(k=2\),这个二分图染色即可。考虑 \(k>2\) 的情况。
手玩一下可以发现,如果有三个点 \(u,v,w\) 两两距离 \(\ge k-1\),则无解。
然后就可以构造了,求出直径,判一下有解条件,此时任意点数为 \(k\) 的简单路径都会经过直径中点,从这个点开始 dfs 染色即可。时间复杂度 \(O(n)\)。
*CF1220G Geolocation
题目描述:给定平面上 \(n\) 个天线 \((x_i,y_i)\)。\(m\) 次询问 \(d_1,d_2,\dots,d_n\),表示某个位置 \((x,y)\) 到每个天线距离的平方排序之后的数组,求所有可能的整点 \((x,y)\),保证有解。
数据范围:\(n\ge 2,nm\le 10^5,x_i,y_i\le 10^8\)。所有输入数据都是非负整数。用于生成数据的 \(x,y\) 在 \([0,10^8]\cap\N\) 中独立均匀随机生成。
solution
经典结论:若将这 \(n\) 个点的重心作为原点,则 \(x^2+y^2=\frac{\sum d_i^2-\sum_{i=1}^n(x_i^2+y_i^2)}n\)。
然后枚举 \((x,y)\) 到 \((x_1,y_1)\) 的距离,求出圆交点并暴力 Check。因为数据随机所以复杂度是对的?
精度误差可能很要死,建议手写个高精分数类并精细实现。
*CF1220F Gardener Alex
题目描述:给定长为 \(n\) 的排列 \(a\),求所有循环移位中小根笛卡尔树的最小深度及取到最小值的方案。
数据范围:\(n\le 2\cdot 10^5\)。
solution
降智大赛?
先倍长成链,容易知道每个元素在笛卡尔树上的子树区间是不变的,求出这个区间,深度即为每个元素被覆盖的次数的最大值。用线段树维护区间加,区间求 \(\max\) 即可。
*CF1223G Wooden Raft
题目描述:给定 \(n\) 个姜米條,你可以切割姜米條但不能拼起来。求在所有切割出两个 \(x\) 和 \(x\) 个 \(y\) 的方案中 \(x\times y\) 的最大值。
数据范围:\(n,a_i\le 5\cdot 10^5\)。
solution
枚举 \(y\),分类讨论两个 \(x\) 在同一块还是不同块,然后枚举 \(\lfloor\frac{2x}y\rfloor\) 或 \(\lfloor\frac xy\rfloor\)。
时间复杂度 \(O(n+V\log V)\)。
CF1225G To Make 1
题目描述:给定 \(n\) 个正整数 \(a_i\) 和正整数 \(k\),定义
每次操作取出两个正整数 \(x,y\),加入 \(f(x+y)\),构造使最终剩下 \(1\) 的方案,需判断无解。
数据范围:\(2\le n\le 16,2\le k,\sum a_i\le 2000,k\nmid a_i\)。
神仙状压
solution
结论:解存在当且仅当存在 \(n\) 个非负整数 \(b_i\) 使得 \(1=\sum_{i=1}^na_ik^{-b_i}\)。
证明:\(\Rightarrow\) 显然,\(\Leftarrow\) 考虑归纳,易得 \(b\) 中最大值所对应的 \(a_i\) 之和被 \(k\) 整除,把最大值合并即可。
于是就可以状压 dp 了,设 \(f_{S,x}\) 表示存在一组 \(b\) 使得 \(\sum_{i\in S}a_ik^{-b_i}=x\)。转移即按照 \(S\) 从小到大,\(x\) 从大到小的顺序,\(f_{S,x}=f_{S,kx}\lor f_{S\backslash\{a_i\},x-a_i}\)。
构造方案即求出 \(b_i\) 再按照从大到小的顺序合并即可。时间复杂度 \(O(\frac{n2^n\sum a_i}w)\)。
#include<bits/stdc++.h>
#define PB emplace_back
#define MP make_pair
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 1<<16, M = 2003;
template<typename T>
void read(T &x){
int ch = getchar(); x = 0; bool f = false;
for(;ch < '0' || ch > '9';ch = getchar());
for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
if(f) x = -x;
}
template<typename T>
bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int n, k, lim, a[16], sum, b[16];
bitset<M> f[N]; priority_queue<pii> pq;
int main(){
read(n); read(k); lim = 1<<n; f[0][0] = 1;
for(int i = 0;i < n;++ i){read(a[i]); sum += a[i];}
for(int S = 1;S < lim;++ S){
for(int i = 0;i < n;++ i) if(S >> i & 1) f[S] |= f[S ^ (1<<i)] << a[i];
for(int i = sum / k;i;-- i) if(f[S][i*k]) f[S][i] = 1;
} if(!f[lim-1][1]){puts("NO"); return 0;}
puts("YES"); int S = lim-1, x = 1;
while(S){
for(;x*k <= sum && f[S][x*k];x *= k)
for(int i = 0;i < n;++ i) if(S >> i & 1) ++ b[i];
for(int i = 0;i < n;++ i)
if(x >= a[i] && (S >> i & 1) && f[S ^ (1<<i)][x - a[i]]){S ^= 1<<i; x -= a[i]; break;}
}
for(int i = 0;i < n;++ i) pq.push(MP(b[i], a[i]));
while(pq.size() > 1){
pii p = pq.top(); pq.pop(); pii q = pq.top(); pq.pop();
printf("%d %d\n", p.se, q.se); p.se += q.se;
while(!(p.se % k)){p.se /= k; -- p.fi;} pq.push(p);
}
}
-CF1237G Balanced Distribution
题目描述:给定正整数 \(k\) 和排成环形,按顺时针编号为 \(0,1,\dots,n-1\) 的非负整数 \(a_i\)。每次操作可以选择相邻 \(k\) 个数进行修改,要求其总和不变。求使得所有 \(a_i\) 相等的最小操作次数及方案。
数据范围:\(2\le k<n\le 10^5,a_i\le 10^4,n|\sum a_i\)。
CF1237H Balanced Reversals
题目描述:给定长为 \(n\) 的 01 字符串 \(S,T\),每次操作选择 \(S\) 的一个长度为偶数的前缀翻转,构造至多 \(n+1\) 次操作将 \(S\) 变为 \(T\),需判断无解。\(T\) 组数据。
数据范围:\(2|n,\sum n\le 4000\)。
solution
首先将连续两个字符看做一个字符,一共四种字符 00,01,10,11。
显然有解的必要条件是 00 和 11 的个数相等。构造证明了它是必要条件。
首先要使 01 和 10 个数分别相等。选择 01 与 10 个数相差比较大的一个,然后翻转其某个前缀。
然后可以维护一段前缀 \([1,i]\) 使得 \(\text{rev}(S[1:i])=T[1:i]\),每次可以通过翻转两次前缀把 \(S[1:j]\) 向右循环移位。最后翻转 \([1,n-2]\) 即可。总操作次数为 \(2(\frac n2-1)+2=n\)。
#include<bits/stdc++.h>
#define PB emplace_back
#define MP make_pair
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 2003;
template<typename T>
void read(T &x){
int ch = getchar(); x = 0; bool f = false;
for(;ch < '0' || ch > '9';ch = getchar());
for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
if(f) x = -x;
}
template<typename T>
bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int T, n, m, a[N], b[N], c[4], d[4], ans[N], tot;
char s1[N<<1], s2[N<<1];
void rev(int *a, int k){
for(int i = (k>>1);i;-- i) swap(a[i], a[k-i+1]);
for(int i = 1;i <= k;++ i) if(a[i] <= 1) a[i] ^= 1;
}
int enc(char x, char y){if(x == '0') return y == '0' ? 2 : 0; return y == '0' ? 1 : 3;}
void solve(){
scanf("%s%s", s1, s2); n = strlen(s1); m = n>>1;
memset(c, 0, sizeof c); memset(d, 0, sizeof d); tot = 0;
for(int i = 0;i < m;++ i){
++c[a[i+1] = enc(s1[i<<1], s1[i<<1|1])];
++d[b[i+1] = enc(s2[i<<1], s2[i<<1|1])];
} if(c[2] != d[2] || c[3] != d[3]){puts("-1"); return;}
int lst = -1;
if(c[0] != d[0]){
if(abs(c[0] - c[1]) > abs(d[0] - d[1])){
for(int i = 1;i <= m;++ i) if(a[i] <= 1){
-- c[a[i]]; ++ c[!a[i]];
if(c[0] == d[0]){rev(a, i); ans[++tot] = i; break;}
}
} else {
for(int i = 1;i <= m;++ i) if(b[i] <= 1){
-- d[b[i]]; ++ d[!b[i]];
if(c[0] == d[0]){rev(b, i); lst = i; break;}
}
}
}
for(int i = 1;i < m;++ i)
for(int j = i;j <= m;++ j)
if(a[j] == b[i]){
if(j > 1){rev(a, j-1); ans[++tot] = j-1;}
rev(a, j); ans[++tot] = j; break;
}
if(m > 1){rev(a, m-1); ans[++tot] = m-1;} if(~lst) ans[++tot] = lst;
printf("%d\n", tot);
for(int i = 1;i <= tot;++ i) printf("%d ", ans[i]<<1); putchar('\n');
}
int main(){read(T); while(T --) solve();}
CODE FESTIVAL 2016 Grand Final
*H AB=C Problem
题目描述:给定定义在 \(\mathbb F_2\) 上的 \(n\times n\) 的矩阵 \(C\),求 \((A,B)\) 的数量\(\bmod(10^9+7)\) 使得 \(AB=C\)。
数据范围:\(n\le 300\)。
solution
好像是三年前的训练题,当时把人吓傻了。
容易得出,固定 \(A\) 之后,若 \(A\) 的列向量张成的线性空间包含 \(C\),则 \(B\) 的数量是 \(2^{n(n-\text{rank}(A))}\)。
现在要对每个 \(i\in[1,n]\) 求出有多少个矩阵 \(A\) 满足:
- \(A\) 张成的线性空间包含 \(C\)。
- \(\text{rank}(A)=i\)。
设 \(r=\text{rank}(C)\),\(f_{i,j}\) 表示 \(i\) 个长为 \(n\) 的向量的秩大小为 \(j\) 的方案数,则 \(A\) 的数量为 \(\frac{f_{n,i}f_{i,r}}{f_{n,r}}\)。
CODE FESTIVAL 2017 qual B
F Largest Smallest Cyclic Shift
题目描述:给定非负整数 \(x,y,z\),构造字典序最大的字符串 \(S\) 使得其含有 \(x\) 个 a
,\(y\) 个 b
,\(z\) 个 c
,且在 \(S\) 的所有循环移位中 \(S\) 最小。
数据范围:\(x+y+z\le 50\)。
solution
假设现在你有 \(k\) 个 Lyndon 串,要将其拼成一个字典序最大的幸运串。
根据 Lyndon 串的性质,对于所有拼成任意串的方法,循环移位中字典序最小的开头一定是组成其的 Lyndon 串的开头。
容易发现,字典序最小的串一定要放最前面。
容易发现,若 \(S,T\) 是 Lyndon 串且 \(S<T\),则 \(S+T\) 是 Lyndon 串。
若当前已有的这些串全相同,则直接全拼起来即可。
若当前已有的这些串不全相同,则直接把字典序最小的串拼上字典序最大的串,然后继续做下去,直到已有的这些串全相同。
使用 multiset 维护,时间复杂度 \(O(n^2\log n)\),其中 \(n=x+y+z\),但是完全跑不满。
CF1017G The Tree
题目描述:给定 \(n\) 个点的树,点有黑白颜色,初始全白。\(q\) 次操作:给定点 \(x\),
- 若 \(x\) 为白色则染黑,否则对儿子递归做此操作。
- 将以 \(x\) 为根的子树染白。
- 查询 \(x\) 的颜色。
数据范围:\(n,q\le 10^5\)。
solution
这题有至少两种做法,虽然我都不会
\(O(n\sqrt n)\):考虑对询问分块,每一块对涉及到的点 \(O(n)\) 建虚树,并且处理出每条边上的白点和黑点个数。虚树上的暴力做,做完之后再推下去。代码简单极了...
\(O(n\log^2n)\):如果暴力做,复杂度偏向修改,考虑稍微平衡一下。
考虑 1 操作时单纯给点 \(x\) 的点权 +1,则操作 \(x\) 会影响子树中的点 \(v\) 当且仅当 \((x,v)\) 这条路径上所有点的点权之和 \(\ge\) \((x,v)\) 的点数。若将初始点权设为 \(-1\) 则 \(v\) 是黑的当且仅当 \((rt,v)\) 的最大后缀和 \(\ge 0\)。可以用线段树维护。
然后考虑 2 操作,首先整棵子树的点权改为 \(-1\),然后要使子树外不对子树内有影响,还要将 \(x\) 减去某个值 \(v\) 使得 \((rt,x)\) 的最大后缀和 \(=-1\),查询一次即可。
还是分块好(雾
#include<bits/stdc++.h>
#define PB emplace_back
#define MP make_pair
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 100003, M = 1<<18;
template<typename T>
void read(T &x){
int ch = getchar(); x = 0; bool f = false;
for(;ch < '0' || ch > '9';ch = getchar());
for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
if(f) x = -x;
}
template<typename T>
bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int n, q, cnt, fa[N], head[N], to[N], nxt[N];
void add(int a, int b){to[++cnt] = b; nxt[cnt] = head[a]; head[a] = cnt;}
int siz[N], wson[N], dep[N], dfn[N], tim, top[N];
void dfs1(int x){
siz[x] = 1;
for(int i = head[x];i;i = nxt[i]){
dep[to[i]] = dep[x] + 1; dfs1(to[i]);
siz[x] += siz[to[i]];
if(siz[to[i]] > siz[wson[x]]) wson[x] = to[i];
}
}
void dfs2(int x, int tp){
top[x] = tp; dfn[x] = ++tim;
if(!wson[x]) return; dfs2(wson[x], tp);
for(int i = head[x];i;i = nxt[i])
if(to[i] != wson[x]) dfs2(to[i], to[i]);
}
struct Node {
int sum, res;
Node(int s = 0, int r = -1e9): sum(s), res(r){}
Node operator + (const Node &o) const {return Node(sum + o.sum, max(o.res, res + o.sum));}
} seg[M];
bool tag[M];
void ptag(int x, int len){tag[x] = true; seg[x].sum = -len; seg[x].res = -1;}
void pdown(int x, int len){if(tag[x]){ptag(x<<1, len+1>>1); ptag(x<<1|1, len>>1); tag[x] = false;}}
void pup(int x){seg[x] = seg[x<<1] + seg[x<<1|1];}
Node qry(int l, int r, int x = 1, int L = 1, int R = n){
if(l <= L && R <= r) return seg[x];
int mid = L + R >> 1; pdown(x, R-L+1);
if(r <= mid) return qry(l, r, x<<1, L, mid);
if(mid < l) return qry(l, r, x<<1|1, mid+1, R);
return qry(l, r, x<<1, L, mid) + qry(l, r, x<<1|1, mid+1, R);
}
void upd(int p, int v, int x = 1, int L = 1, int R = n){
if(L == R){seg[x].sum += v; seg[x].res += v; return;}
int mid = L + R >> 1; pdown(x, R-L+1);
if(p <= mid) upd(p, v, x<<1, L, mid);
else upd(p, v, x<<1|1, mid+1, R); pup(x);
}
void cov(int l, int r, int x = 1, int L = 1, int R = n){
if(l <= L && R <= r){ptag(x, R-L+1); return;}
int mid = L + R >> 1; pdown(x, R-L+1);
if(l <= mid) cov(l, r, x<<1, L, mid);
if(mid < r) cov(l, r, x<<1|1, mid+1, R); pup(x);
}
int qry(int x){
Node res;
while(top[x] > 1){res = qry(dfn[top[x]], dfn[x]) + res; x = fa[top[x]];}
return (qry(1, dfn[x]) + res).res;
}
int main(){
read(n); read(q); ptag(1, n);
for(int i = 2;i <= n;++ i){read(fa[i]); add(fa[i], i);}
dfs1(1); dfs2(1, 1);
while(q --){
int opt, x; read(opt); read(x);
if(opt == 1) upd(dfn[x], 1);
else if(opt == 2){
cov(dfn[x], dfn[x] + siz[x] - 1);
upd(dfn[x], - qry(x) - 1);
} else puts(qry(x) >= 0 ? "black" : "white");
}
}
-CF1028F Make Symmetrical
题目描述:维护 \(S\subseteq \{1,2,\dots,112904\}^2\),\(q\) 次操作给定 \(x,y\):
- \(S\) 中加入 \((x,y)\),保证操作前 \((x,y)\notin S\)。
- \(S\) 中删去 \((x,y)\),保证操作前 \((x,y)\in S\)。
- 求至少需添加多少点才能使得 \(S\) 关于 \((0,0),(x,y)\) 连线对称。询问不加点且独立。
数据范围:\(q\le 2\cdot 10^5\)。
CF1205E Expected Value Again
题目描述:给定字符串长度 \(n\) 和字符集大小 \(k\),设 \(X\) 为 border 个数(不含本身),求 \(\mathbb E[X^2]\bmod(10^9+7)\)。
数据范围:\(n\le 10^5,k\le 10^9\)。
solution
长度为 \(x\) 的 border 等价于 \(n-x\) 的非严格周期。
显然只能钦定两个循环节 \(p,q\) 算方案数。
Periodicity Lemma
若字符串 \(S\) 有循环节 \(p,q\),且 \(p+q\le |S|+\gcd(p,q)\),则 \(\gcd(p,q)\) 也是循环节。
考虑一个样例 \(S=\) abacaba
,\(p=6,q=4\),而 \(\gcd(p,q)=2\) 不是循环节,所以下界是紧的。
证:不妨设 \(p>q\) 且下标从 \(1\) 开始。
考虑归纳,奠基显然成立。
对于 \(i\le |S|-p\),有 \(S_i=S_{i+p}=S_{i+p-q}\),所以 \(p-q\) 是长度为 \(|S|-q\) 的前缀的循环节,后缀同理。
因为 \((p-q)+q\le(|S|-q)+\gcd(p-q,q)\),根据归纳可知 \(\gcd(p,q)\) 是长度为 \(|S|-q\) 的前缀的循环节,后缀同理。
因为 \(\gcd(p,q)|(p-q)\),所以 \(\gcd(p,q)\le p-q\),所以 \(p+q\le |S|+p-q\),所以 \(q\le |S|-q\),所以 \(\gcd(p,q)\) 是长度为 \(q\) 的前缀的循环节,又因为 \(\gcd(p,q)|q\) 所以循环节是整的,且 \(\gcd(p,q)\) 是长度为 \(|S|-q\) 的后缀的循环节,所以 \(\gcd(p,q)\) 是原串的循环节,Q.E.D.
计算概率公式
考虑另一种情况,当 \(p+q>n+\gcd(p,q)\) 时,证明连 \((x,x+p)\) 和 \((x,x+q)\) 这些边之后无环。
首先连上 \((x,x+p)\) 并对连通块缩点,形成\(\bmod p\) 的同余类,然后连上 \((x,(x+q)\bmod p)\),形成 \(\gcd(p,q)\) 个环,这些环必定会经过 \((p-\gcd(p,q),p]\) 中的至少一个点,如果其不能向后连边则此环不完整,所以无环。
得到连通块个数即为点数-边数,即方案数为 \(k^{p+q-n}\)。
于是这题就只剩下数论推柿子部分了(!
把答案表示成一个 \(k\) 的多项式的形式,其他两项都很好算,暴力即可,时间复杂度 \(O(n\log n)\)。
#include<bits/stdc++.h>
#define PB emplace_back
using namespace std;
typedef long long LL;
const int N = 100003, mod = 1e9 + 7;
int ksm(int a, int b){
int res = 1;
for(;b;b >>= 1, a = (LL)a * a % mod)
if(b & 1) res = (LL)res * a % mod;
return res;
}
void qmo(int &x){x += x >> 31 & mod;}
int n, k, a[N<<1], b[N<<1], pw[N], mu[N], pri[N], tot, ans;
bool notp[N]; vector<int> vec[N];
int main(){
scanf("%d%d", &n, &k);
notp[0] = notp[1] = true; mu[1] = pw[0] = 1;
for(int i = 1;i <= n;++ i) pw[i] = (LL)pw[i-1] * k % mod;
for(int i = n;i <= (n-1<<1);++ i) a[i] = (n<<1) - i - 1;
for(int i = 2;i <= n;++ i){
if(!notp[i]){pri[tot++] = i; mu[i] = mod - 1;}
for(int j = 0;j < tot && i * pri[j] <= n;++ j){
notp[i * pri[j]] = true;
if(i % pri[j]) mu[i * pri[j]] = mod - mu[i];
else break;
}
} for(int i = 1;i <= n;++ i)
for(int j = i;j <= n;j += i) vec[j].PB(i);
for(int p = 1;p < n;++ p){
int l = (n-1<<1) / p; memset(b+1, 0, l+1<<2);
for(int d : vec[p]){
int m = (n + d - 1) / p;
qmo(b[m] += mu[p / d] - mod);
ans = (ans + (m * (m-1ll) >> 1) % mod * pw[d] % mod * mu[p / d]) % mod;
} for(int i = l-1;i;-- i) qmo(b[i] += b[i+1] - mod);
for(int i = 1;i <= l;++ i) qmo(a[i * p] -= b[i] * (i-1ll) % mod);
} for(int i = n;i <= (n-1<<1);++ i) ans = (ans + (LL)a[i] * pw[i-n]) % mod;
printf("%lld\n", (LL)ans * ksm(k, mod-1-n) % mod);
}
*CF1242E Planar Perimeter
题目描述:给定 \(f\) 个正整数 \(a_i\),构造简单平面图,边权为 \(1\),有 \(f\) 个内部区域,每个内部区域的周长分别为 \(a_i\),且外围周长尽可能小。
数据范围:\(a_i\ge 3,\sum a_i\le 3\cdot 10^5\)。
Gym102576
*A Bags of Candies
题目描述:求 \(V=[1,n]\cap\N,E=\{(i,j)\mid \gcd(i,j)>1\}\) 的最大匹配。
数据范围:\(n\le 10^{11}\)。
solution
这好数竞啊(
首先 \(1\) 和 \(>\frac n2\) 的质数必定无法配对,然后其他的必定可以匹配到至多只剩一个点。
设 \(f(x)\) 表示 \(x\) 的最大质因子,把每个点按 \(f(x)\) 分组,每一组两两可以匹配。最后会剩下一个,我们剩下 \(2\cdot f(x)\) 就可以保证最后的还能继续匹配。
答案即为 \(\Big\lfloor\dfrac{n-1-\pi(n)+\pi(\frac n2)}{2}\Big\rfloor\),用 min_25 筛算算即可。
*B Binomial
题目描述:给定 \(n\) 个正整数 \(a_i\),求 \(\sum_{i=1}^n\sum_{j=1}^n(\binom{a_i}{a_j}\bmod 2)\)。
数据范围:\(n,a_i\le 10^6\)。
solution
众所周知的 Lucas 定理,枚举 \(a_i\),用 FMT 算即可。
-C Bookface
题目描述:给定 \(n\) 个自然数 \(c_i\) 和正整数 \(d\),每次操作可以将 \(c_i\) 加 \(1\) 或减 \(1\)(但 \(0\) 不能减),使得 \(\forall 1\le i<j\le n,|c_i-c_j|\ge d\)。\(T\) 组数据。
数据范围:\(T\le 10^5,n\le 2\cdot 10^5,\sum n\le 10^6,c_i\le3\cdot 10^{11}\)。
-D Clique
题目描述:给定长为 \(10^6\) 的环上 \(n\) 个区间 \([l_i,r_i]\),求“区间图”的最大环大小。多组数据。
数据范围:\(n\le 3000,\sum n\le 24000,\text{TL}=25\text s\)。
K To argue, or not to argue
题目描述:给定 \(n\times m\) 的网格图,去掉若干个点,求分配 \(k\) 对有区别的位置的方案数\(\bmod(10^9+7)\),使得每一对位置不相邻。\(T\) 组数据。
数据范围:\(T\le 10,2k\le nm\le 144,\text{TL}=10\text s\)。
solution
二项式反演,设 \(f_k\) 表示钦定 \(k\) 对位置相邻的方案数,共有 \(a\) 个可选的位置,则
\(f_k\) 可以用轮廓线 dp 求出,时间复杂度 \(O(nmk2^{\min(n,m)})\)。
#include<bits/stdc++.h>
#define PB emplace_back
#define MP make_pair
#define fi first
#define se second
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 144, M = 12, K = N>>1, mod = 1e9 + 7;
int ksm(int a, int b){
int res = 1;
for(;b;b >>= 1, a = (LL)a * a % mod)
if(b & 1) res = (LL)res * a % mod;
return res;
}
void qmo(int &x){x += x >> 31 & mod;}
template<typename T>
void read(T &x){
int ch = getchar(); x = 0; bool f = false;
for(;ch < '0' || ch > '9';ch = getchar());
for(;ch >= '0' && ch <= '9';ch = getchar()) x = x * 10 + ch - '0';
if(f) x = -x;
}
template<typename T>
bool chmax(T &a, const T &b){if(a < b) return a = b, 1; return 0;}
template<typename T>
bool chmin(T &a, const T &b){if(a > b) return a = b, 1; return 0;}
int T, n, m, k, lim, tot, fac[N+1], inv[N+1], dp[2][K][1<<M];
bool a[N][M];
void solve(){
read(n); read(m); read(k); tot = 0;
for(int i = 0;i < n;++ i)
for(int j = 0, ch;j < m;++ j){
do ch = getchar(); while(ch != '.' && ch != 'X');
tot += ((n < m ? a[j][i] : a[i][j]) = (ch == '.'));
}
if(tot < (k<<1)){puts("0"); return;}
if(n < m) swap(n, m); lim = 1<<m;
memset(dp[0], 0, sizeof dp);
int cur = 0, ans = 0; dp[0][0][0] = 1;
for(int i = 0;i < n;++ i)
for(int j = 0;j < m;++ j, cur ^= 1){
memset(dp[!cur], 0, sizeof dp[!cur]);
for(int x = 0;x <= k;++ x)
for(int S = 0;S < lim;++ S){
int &tmp = dp[!cur][x][S];
qmo(tmp += dp[cur][x][S & ~(1<<j)] - mod);
if(x){
if(i && a[i][j] && a[i-1][j] && !(S>>j&1))
qmo(tmp += dp[cur][x-1][S|(1<<j)] - mod);
if(j && a[i][j] && a[i][j-1] && !(S>>(j-1)&3))
qmo(tmp += dp[cur][x-1][S|(1<<j-1)] - mod);
}
}
}
for(int i = 0, pw = 1;i <= k;++ i, pw = pw * (mod-2ll) % mod)
ans = (ans + (LL)pw * fac[tot-2*i] % mod * inv[k-i] % mod * dp[cur][i][0]) % mod;
printf("%lld\n", (LL)ans * fac[k] % mod * inv[tot-2*k] % mod);
}
int main(){
read(T); fac[0] = 1;
for(int i = 1;i <= N;++ i) fac[i] = (LL)fac[i-1] * i % mod;
inv[N] = ksm(fac[N], mod-2);
for(int i = N;i;-- i) inv[i-1] = (LL)inv[i] * i % mod;
while(T --) solve();
}
Gym102586
*C Sum Modulo
题目描述:给定 \(n\) 个正整数 \(a_i\) 和正整数 \(m,k\),对于初始为 \(k\) 的自然数 \(X\),每次操作以 \(\frac{a_i}{\sum a_i}\) 的概率生成 \(i\) 并将 \(X\) 变为 \((X-i)\bmod m\),求将 \(X\) 变为 \(0\) 的期望步数\(\bmod 998244353\)。
数据范围:\(n\le\min(500,m-1),m\le 10^{18},k<m,a_i\le 100\)。
solution
设 \(f_k\) 表示答案,\(f_0=0\)。用主元法解方程,最后是一个线性递推,时间复杂度 \(O(n^3+n^2\log m)\)。
-G Matrix Inversion
题目描述:给定自然数 \(n,x,y\),构造 \(n\times n\) 的矩阵 \(M\),其中 \(1,2,\dots,n^2\) 各出现一次,且 \(A_{(i-1)n+j}=M_{i,j}\) 的序列 \(A\) 的逆序对数为 \(x\),\(B_{(i-1)n+j}=M_{j,i}\) 的序列 \(B\) 的逆序对数为 \(y\)。需判断无解。
数据范围:\(n\le 300,x,y\le\binom{n^2}2\)。
*UOJ308 新年拯救计划
题目描述:给定 \(n\) 个点 \(m\) 条边的无向图,求 \(k\)-染色数\(\bmod 6\)。\(T\) 组数据。
数据范围:\(T\le 5,n\le 10^5,m\le 2\cdot 10^5,k\le 10^4,\) 无自环。
solution
众所周知,色多项式是整系数多项式,而 \(\forall n\in\Z,F(x)\in\Z[x],p\in\N_+[F(n)\equiv F(n\bmod p)\pmod p]\),所以只用求 \((k\bmod 2)\)-染色数\(\bmod 2\) 和 \((k\bmod 3)\)-染色数\(\bmod 3\),然后再 CRT 合并即可。
这显然有手就行,时间复杂度 \(O(n+m)\)。