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\),定义

\[f(x)=\begin{cases}f(\frac xk),&k|x \\x,&\text{otherwise}\end{cases} \]

每次操作取出两个正整数 \(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\)

  1. \(x\) 为白色则染黑,否则对儿子递归做此操作。
  2. 将以 \(x\) 为根的子树染白。
  3. 查询 \(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\)

  1. \(S\) 中加入 \((x,y)\),保证操作前 \((x,y)\notin S\)
  2. \(S\) 中删去 \((x,y)\),保证操作前 \((x,y)\in S\)
  3. 求至少需添加多少点才能使得 \(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}\)

于是这题就只剩下数论推柿子部分了(!

\[\begin{aligned} k^n\times Ans=&\sum_{i=1}^{n-1}\sum_{j=1}^{n-1}k^{\max(i+j-n,\gcd(i,j))} \\ =&\sum_{i=1}^{n-1}\sum_{j=1}^{n-1}k^{i+j-n}+\sum_{i=1}^{n-1}\sum_{j=1}^{n-1}(k^{\gcd(i,j)}-k^{i+j-n})[i+j<n+\gcd(i,j)] \\ &\sum_{i=1}^{n-1}\sum_{j=1}^{n-1}k^{i+j-n}[i+j<n+\gcd(i,j)]\\ =&\sum_{p=1}^{n-1}\sum_{d|p}\mu(\frac pd)\sum_{i=1}^{\lfloor\frac{n-1}p\rfloor}\sum_{j=1}^{\lfloor\frac{n-1}p\rfloor}k^{(i+j)p-n}\left[i+j\le\lfloor\frac{n+d-1}p\rfloor\right] \\ =&\sum_{p=1}^{n-1}\sum_{d|p}\mu(\frac pd)\sum_{x=1}^{\lfloor\frac{n+d-1}p\rfloor}(x-1)k^{px-n} \end{aligned} \]

把答案表示成一个 \(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\) 个可选的位置,则

\[Ans=\frac{k!}{(a-2k)!}\sum_{i=0}^k\frac{(a-2i)!}{(k-i)!}(-2)^if_i \]

\(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)\)

posted @ 2021-02-13 15:05  mizu164  阅读(615)  评论(0编辑  收藏  举报