CSP-S 模拟 19/11/02

在这里插入图片描述
n ≤ 2333 n\le2333 n2333
将最后形成的三元组分类
三个一样的,两个一样的,三个都不一样的分别计算就比较方便
三个一样的和两个一样的可以 O ( n ) O(n) O(n)
三个都不一样显然枚举两个然后算出另一个


关于手写一些可以实现 m a p map map功能的东西
可以用一个类似邻接表的东西存

	int first[Mod], nxt[N], to[N], val[N], tot;  
	int ask(int p){
		int x = p % Mod;
		for(int i = first[x]; i; i = nxt[i]) if(to[i] == p) return val[i];
		return 0;
	}
	void add(int p, int v){
		int x = p % Mod;
		for(int i = first[x]; i; i = nxt[i]){ if(to[i] == p) {val[i] += v; return;} }
		nxt[++tot] = first[x], first[x] = tot, to[tot] = p, val[tot] = v;
	}

也可以冲突就暴力挪位

struct MAP{
	static cs int Mod = 1e7+9;
	int key[Mod], val[Mod];
	Map(){ memset(key, -1, sizeof(key)); }
	int pos(int k){
		int h = k % Mod;
		while(key[h] != -1 && key[h] != k) h = h+1 == Mod ? 0 : h+1;
		return h;
	}
	void ins(int k){
		int h = pos(k); 
		if(key[h] == -1) key[h] = k, val[h] = 1;
		else val[h]++;
	}
	int find(int k){
		int h = pos(k);
		return key[h] == -1 ? 0 : val[h];
	}
};

也可以离散化然后暴力二分

struct MAP{
	int b[N], siz, val[N];
	void build(){
		sort(b + 1, b + siz + 1); 
		siz = unique(b + 1, b + siz + 1) - (b + 1);
	}
	int find(int x){
		int l = 1, r = siz;
		while(l < r){
			int mid = (l+r) >> 1;
			if(b[mid] >= x) r = mid; else l = mid + 1;
		} return l;
	}
	void add(int x){ val[find(x)]++; }
	int ask(int x){ int l = find(x); if(b[l] == x) return val[l]; return 0; }
};

然后发现快速幂的复杂度是满的 l o g ( 1 e 9 ) log(1e9) log(1e9),可以改成常数更小的 e x g c d exgcd exgcd
还可以预处理逆元,然后 n 2 n^2 n2 枚举的时候就不用快速幂只剩下一个常数很小的二分
复杂度 O ( n 2 l o g ( n ) ) O(n^2log(n)) O(n2log(n))

#include<bits/stdc++.h>
#define cs const
using namespace std;
namespace IO{
	inline char gc(){
		static cs int Rlen = 1 << 22 | 1;
		static char buf[Rlen], *p1, *p2;
		return (p1 == p2) && (p2 = (p1 = buf) + fread(buf, 1, Rlen, stdin), p1 == p2) ? EOF : *p1++;
	}
	int read(){
		int cnt = 0, f = 1; char ch = 0;
		while(!isdigit(ch)) { ch = gc(); if(ch == '-') f = -1;}
		while(isdigit(ch)) cnt = cnt * 10 + (ch-'0'), ch = gc();
		return cnt * f;
	}
}
using namespace IO;	
typedef long long ll;
cs int N = 2500;
int n, p, a[N], b[N], siz, inv[N]; ll ans;
int add(int a, int b){ return a + b >= p ? a + b - p : a + b; }
int mul(int a, int b){ return 1ll * a * b % p; }
int ksm(int a, int b){ int ans=1; for(;b;b>>=1, a=mul(a,a)) if(b&1) ans=mul(ans,a); return ans; }
bool vis[N]; int ps[N], tim[N];
struct MAP{
	int b[N], siz;
	int val[N];
	void build(){
		sort(b + 1, b + siz + 1); 
		siz = unique(b + 1, b + siz + 1) - (b + 1);
	}
	int find(int x){
		int l = 1, r = siz;
		while(l < r){
			int mid = (l+r) >> 1;
			if(b[mid] >= x) r = mid; else l = mid + 1;
		} return l;
	}
	void add(int x){ val[find(x)]++; }
	int ask(int x){ int l = find(x); if(b[l] == x) return val[l]; return 0; }
}bin, sum;
int main(){
	n = read(), p = read();
	for(int i = 1; i <= n; i++){
		a[i] = read(); 
		bin.b[++bin.siz] = a[i]; 
		sum.b[++sum.siz] = a[i] % p;
		b[++siz] = a[i];
		inv[i] = ksm(a[i] % p, p - 2);
	}
	sort(b + 1, b + siz + 1); 
	siz = unique(b + 1, b + siz + 1) - (b + 1);
	bin.build();
	sum.build();
	for(int i = 1; i <= n; i++) 
		ps[i] = bin.find(a[i]); 
	for(int i = 1; i <= n; i++){
		if(!tim[ps[i]]) sum.add(a[i] % p);
		tim[ps[i]]++;
	}
	// three of them are the same
	for(int i = 1; i <= n; i++){
		if(tim[ps[i]] >= 3 && !vis[ps[i]]){
			vis[ps[i]] = true;
			if(mul(inv[i], mul(inv[i], inv[i])) == 1) ++ans;
		}
	} 
	// two of them are the same
	memset(vis, 0, sizeof(vis));
	for(int i = 1; i <= n; i++){
		if(tim[ps[i]] >= 2 && !vis[ps[i]]){
			vis[ps[i]] = true;
			int nx = mul(inv[i], inv[i]);
			ans += sum.ask(nx);
			if(a[i] % p == nx) ans--;
		}
	} 
	// each of them is different 
	memset(vis, 0, sizeof(vis));
	ll ret = 0;
	for(int i = 1; i <= siz; i++) inv[i] = ksm(b[i] % p, p - 2);
	for(int i = 1; i <= siz; i++){
		for(int j = i + 1; j <= siz; j++){
			int ni = b[i] % p, nj = b[j] % p;
			int nx = mul(inv[i], inv[j]);
			ret += sum.ask(nx);
			if(ni == nx) ret--;
			if(nj == nx) ret--;
		}
	} cout << ans + ret / 3; return 0;
}

在这里插入图片描述
n ≤ 1 e 5 n\le 1e5 n1e5
考场的垃圾做法:
发现物品按 w w w 排序就是选一个 v v v 的最长不下降序列并且满足选出来的每一个 w w w 在另一边都有一个 ≥ \ge 它的映射,另一边也排个序, f i , j f_{i,j} fi,j 表示这边选到 i i i另一边选到 j j j 的最长长度
f i , j = m a x ( f p , k ) ( v p ≤ v i , k ≤ j − 1 ) f_{i,j}=max(f_{p,k})(v_p\le v_i,k\le j-1) fi,j=max(fp,k)(vpvi,kj1),二维树状数组优化转移


正解:考虑贪心,显然如果最后的长度是 l e n len len 的话选最大的 l e n len len 个一定是最优的
考虑对每一个 i i i 求出它向后的最长长度 l e n len len 并且求出可以装下 i i i 的个数 c n t cnt cnt,那么合法的最长长度就是 m i n ( l e n , c n t ) min(len,cnt) min(len,cnt),从后向前 d p dp dp,动态维护装的下的个数,树状数组优化转移即可
有时候不能莽起 dp,还是要从贪心的角度考虑

#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
	int x = 0, f = 1; char c = 0;
	while(!isdigit(c)){ c = getchar(); if(c == '-')f = -1;}
	while(isdigit(c)) x = x*10 + (c-'0'), c = getchar();
	return x * f;
}
typedef long long ll;
cs int N = 1e5 + 5;
int T, n, m, t[N], b[N], siz;
struct BAG{ int w, v;}a[N];
bool cmp(BAG a, BAG b){ return a.w < b.w || (a.w == b.w && a.v < b.v); }
struct BIT{
	int c[N]; void clear(){ memset(c, 0, sizeof(c)); }
	void add(int x, int v){ for(;x;x-=x&-x) c[x] = max(c[x], v); }
	int ask(int x){ int ans=0; for(;x<=siz;x+=x&-x) ans = max(ans, c[x]); return ans; }
}bit;
void Solve(){
	bit.clear(); siz = 0;
	n = read();
	for(int i = 1; i <= n; i++){
		a[i].w = read(); a[i].v = read(); 
		b[++siz] = a[i].v;
	} sort(a + 1, a + n + 1, cmp);
	reverse(a + 1, a + n + 1);
	sort(b + 1, b + siz + 1); siz = unique(b + 1, b + siz + 1) - (b + 1);
	m = read();
	for(int i = 1; i <= m; i++){
		t[i] = read();
	} sort(t + 1, t + m + 1);
	reverse(t + 1, t + m + 1);
	int ans = 0, p = 0;
	for(int i = 1; i <= n; i++){
		while(p + 1 <= m && t[p + 1] >= a[i].w) ++p;
		int x = lower_bound(b + 1, b + siz + 1, a[i].v) - b;
		int f = min(bit.ask(x) + 1, p);
		bit.add(x, f); ans = max(ans, f);
	} cout << ans << '\n';
}
int main(){
	T = read(); while(T--) Solve(); return 0;
}

在这里插入图片描述
n ≤ 500 n\le 500 n500


考场的垃圾做法:
f s i z , d e p f_{siz,dep} fsiz,dep 表示大小和深度为 s i z , d e p siz,dep siz,dep 的方案数,暴力枚举拆分,以及拆分出来的深度
f s i z , d e p = ∑ i = 1 s i z − 1 ∑ j = 1 s i z − i − 1 f s i z − i − 1 , j ∑ k = 1 i f i , k [ m a x ( j , k + 1 ) = 1 ] f_{siz,dep}=\sum_{i=1}^{siz-1}\sum_{j=1}^{siz-i-1}f_{siz-i-1,j}\sum_{k=1}^{i}f_{i,k}[max(j,k+1)=1] fsiz,dep=i=1siz1j=1sizi1fsizi1,jk=1ifi,k[max(j,k+1)=1]
然后还要重新分配编号,考场上思维不严谨的我写的 ( s i z − 1 k ) \binom{siz-1}{k} (ksiz1)
请睁大眼睛看一下我的预处理

	c[0][0] = 1;
	for(int i = 1; i <= n; i++){ c[i][i] = 1;
		for(int j = 1; j <= i; j++){
			c[i][j] = add(c[i-1][j], c[i-1][j-1]);
		}
	}

手玩一下发现这样预处理正好把杨辉三角挪了一位,所以实际上乘的系数是 ( s i z − 2 k − 1 ) \binom{siz-2}{k-1} (k1siz2)
s t d std std 正好成的是 ( s i z − 2 k − 1 ) \binom{siz-2}{k-1} (k1siz2),然后我就歪打正着了(雾)
言归正传,钦定是原来的取到 d e p dep dep 还是后来拼接的取到 d e p dep dep 就可以前缀和优化转移


说一下乘 ( s i z − 1 k ) \binom{siz-1}{k} (ksiz1) 为什么有问题,因为当前拼接的子树很有可能是跟之前的同构,显然会算重
我们直接钦定当前拼接上去的子树的根是次小,这样相当于对儿子进行了排序,这样就不会算重了
既然钦定了根是最小,当前子树的根是次小,那么方案数就是将剩下的重新排列的方案数即 ( s i z − 2 k − 1 ) \binom{siz-2}{k-1} (k1siz2)
然后发现前缀和非常丑,直接按套路把状态定义为大小为 s i z siz siz 深度 ≤ d e p \le dep dep 的方案数
f s i z , d e p = ∑ i = 1 s i z − 1 f s i z − i − 1 , d e p ∗ f i , d e p − 1 ∗ ( s i z − 2 i − 1 ) f_{siz,dep}=\sum_{i=1}^{siz-1}f_{siz-i-1,dep}*f_{i,dep-1}*\binom{siz-2}{i-1} fsiz,dep=i=1siz1fsizi1,depfi,dep1(i1siz2)


考虑直接枚举子树的拼接,枚举子树个数以及每个子树的大小
f [ d ] [ n ] = ∑ k = 1 n − 1 1 k ! ∑ ∑ i = 1 k b i = n − 1 ( n − 1 ) ! ∏ b i ! ∏ i = 1 k f [ d − 1 ] [ b i ] f[d][n]=\sum_{k=1}^{n-1}\frac{1}{k!}\sum_{\sum_{i=1}^k b_i=n-1}\frac{(n-1)!}{\prod b_i!}\prod_{i=1}^kf[d-1][b_i] f[d][n]=k=1n1k!1i=1kbi=n1bi!(n1)!i=1kf[d1][bi]
f [ d ] [ n ] ( n − 1 ) ! = ∑ k = 1 n − 1 1 k ! ∑ ∑ i = 1 k b i = n − 1 1 ∏ b i ! ∏ i = 1 k f [ d − 1 ] [ b i ] \frac{f[d][n]}{(n-1)!}=\sum_{k=1}^{n-1}\frac{1}{k!}\sum_{\sum_{i=1}^k b_i=n-1}\frac{1}{\prod b_i!}\prod_{i=1}^kf[d-1][b_i] (n1)!f[d][n]=k=1n1k!1i=1kbi=n1bi!1i=1kf[d1][bi]
注意到后面一坨的组合意义是集合间带标号的拼接,上指数型生成函数
f d ( x ) = ∑ i = 0 ∞ f [ d ] [ i ] i ! x i f_d(x)=\sum_{i=0}^{\infty}\frac{f[d][i]}{i!}x^i fd(x)=i=0i!f[d][i]xi
∑ n = 0 ∞ f [ d ] [ n ] ( n − 1 ) ! x n − 1 = ∑ n = 0 ∞ ∑ k = 1 n − 1 1 k ! ∑ ∑ i = 1 k b i = n − 1 ∏ i = 1 k f [ d − 1 ] [ b i ] b i ! x b i \sum_{n=0}^{\infty}\frac{f[d][n]}{(n-1)!}x^{n-1}=\sum_{n=0}^{\infty}\sum_{k=1}^{n-1}\frac{1}{k!}\sum_{\sum_{i=1}^k b_i=n-1}\prod_{i=1}^k\frac{f[d-1][b_i]}{b_i!}x^{b_i} n=0(n1)!f[d][n]xn1=n=0k=1n1k!1i=1kbi=n1i=1kbi!f[d1][bi]xbi
= ∑ k = 1 ∞ 1 k ! ∑ b i = 0 ∞ ∏ i = 1 k f [ d − 1 ] [ b i ] b i ! x b i =\sum_{k=1}^{\infty} \frac{1}{k!}\sum_{b_i=0}^{\infty}\prod_{i=1}^k\frac{f[d-1][b_i]}{b_i!}x^{b_i} =k=1k!1bi=0i=1kbi!f[d1][bi]xbi
= ∑ k = 1 ∞ 1 k ! ∏ i = 1 k ∑ b i = 0 ∞ f [ d − 1 ] [ b i ] b i ! x b i =\sum_{k=1}^{\infty} \frac{1}{k!}\prod_{i=1}^k\sum_{b_i=0}^{\infty}\frac{f[d-1][b_i]}{b_i!}x^{b_i} =k=1k!1i=1kbi=0bi!f[d1][bi]xbi
= ∑ k = 1 ∞ 1 k ! f d − 1 ( x ) k = e x p ( f d − 1 ( x ) ) =\sum_{k=1}^{\infty} \frac{1}{k!}f_{d-1}(x)^k=exp(f_{d-1}(x)) =k=1k!1fd1(x)k=exp(fd1(x))

∑ n = 0 ∞ f [ d ] [ n ] ( n − 1 ) ! x n − 1 = f d ( x ) ′ = e x p ( f d − 1 ( x ) ) \sum_{n=0}^{\infty}\frac{f[d][n]}{(n-1)!}x^{n-1}=f_d(x)'=exp(f_{d-1}(x)) n=0(n1)!f[d][n]xn1=fd(x)=exp(fd1(x))
所以
f d ( x ) = ∫ e x p ( f d − 1 ( x ) ) d x f_d(x)=\int exp(f_{d-1}(x)) dx fd(x)=exp(fd1(x))dx
多项式全家桶


#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
	int x = 0, f = 1; char c = 0;
	while(!isdigit(c)){ c = getchar(); if(c == '-')f = -1;}
	while(isdigit(c)) x = x*10 + (c-'0'), c = getchar();
	return x * f;
}
typedef long long ll;
cs int N = 505;
int n, k, L, R;
cs int Mod = 998244353;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
void Add(int &a, int b){ a = add(a, b); }
int c[N][N];
bool ban[N];
int f[N][N];
int main(){
	n = read();
	k = read();
	for(int i = 1; i <= k; i++) ban[read()] = true;
	L = read();
	R = read();
	c[0][0] = 1;
	for(int i = 1; i <= n; i++){ 
		c[i][0] = 1;
		for(int j = 1; j <= i; j++){
			c[i][j] = add(c[i-1][j], c[i-1][j-1]);
		}
	} 
	f[1][1] = ban[1] ? 0 : 1;
	for(int dep = 2; dep <= n; dep++){
		for(int siz = 1; siz <= n; siz++){
			if(siz == 1){ f[siz][dep] = 1; continue; }
			for(int k = 1; k < siz; k++){
				Add(f[siz][dep], mul(c[siz - 2][k - 1], mul(f[siz - k][dep], f[k][dep - 1])));
			} 	
		} for(int siz = 1; siz <= n; siz++) if(ban[siz]) f[siz][dep] = 0;
	}
	for(int i = L; i <= R; i++) cout << add(f[n][i], Mod - f[n][i-1]) << " ";	
	return 0;
}
#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
	int x = 0, f = 1; char c = 0;
	while(!isdigit(c)){ c = getchar(); if(c == '-')f = -1;}
	while(isdigit(c)) x = x*10 + (c-'0'), c = getchar();
	return x * f;
}
typedef long long ll;
cs int N = 505;
int n, k, L, R;
cs int Mod = 998244353;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
void Add(int &a, int b){ a = add(a, b); }
int ksm(int a, int b){ int ans = 1; for(;b;b>>=1, a = mul(a, a)) if(b&1) ans = mul(ans, a); return ans; }
int a[N];
#define poly vector<int>
poly f[N];
cs int K = 13;
poly w[K + 1];
int inv[1 << K | 5];
int bit, up, rev[1 << K | 5];
void prework(){
	for(int i = 1; i <= K; i++) w[i].resize(1 << i - 1);
	int wn = ksm(3, (Mod-1)/(1<<K)); w[K][0] = 1;
	for(int i = 1; i < (1 << K-1); i++) w[K][i] = mul(w[K][i-1], wn);
	for(int i = K-1; i; i--)
		for(int j = 0; j < (1 << i-1); j++) w[i][j] = w[i + 1][j << 1];
	inv[0] = inv[1] = 1;
	for(int i = 2; i <= (1 << K); i++) inv[i] = mul(Mod-Mod/i, inv[Mod%i]);
}
void init(int len){
	bit = 0; up = 1; while(up < len) up <<= 1, bit++;
	for(int i = 0; i < up; i++) rev[i] = (rev[i>>1]>>1) | ((i&1)<<(bit-1));
}
void NTT(poly &a, int typ){
	for(int i = 0; i < up; i++) if(i < rev[i]) swap(a[i], a[rev[i]]);
	for(int i = 1, l = 1; i < up; i <<= 1, l++)
		for(int j = 0; j < up; j += (i << 1))
			for(int k = 0; k < i; k++){
				int x = a[k + j], y = mul(w[l][k], a[k + j + i]);
				a[k + j] = add(x, y); a[k + j + i] = add(x, Mod - y);
			}
	if(typ == -1){
		reverse(a.begin() + 1, a.end()); 
		for(int i = 0; i < up; i++) a[i] = mul(a[i], inv[up]); 
	}
}
poly operator * (poly a, poly b){
	int len = a.size() + b.size() - 1;
	init(len);
	a.resize(up); b.resize(up);
	NTT(a, 1); NTT(b, 1);
	for(int i = 0; i < up; i++) a[i] = mul(a[i], b[i]);
	NTT(a, -1); a.resize(len); return a;
}
poly Inv(poly a, int lim){
	int n = a.size(); poly c, b(1, ksm(a[0], Mod-2));
	for(int len = 4; (len >> 2) < lim; len <<= 1){
		init(len); c.resize(len >> 1);
		for(int i = 0; i < (len >> 1); i++) c[i] = i < n ? a[i] : 0;
		c.resize(up); b.resize(up);
		NTT(c, 1); NTT(b, 1);
		for(int i = 0; i < up; i++) b[i] = mul(b[i], add(2, Mod - mul(b[i], c[i])));
		NTT(b, -1); b.resize(len >> 1);
	} b.resize(lim); 
	return b;
}
poly deriv(poly a){
	for(int i = 0; i+1 < a.size(); i++) a[i] = mul(a[i + 1], i + 1);
	a.pop_back(); return a;
}
poly integ(poly a){
	a.push_back(0);
	for(int i = a.size() - 1; i; i--) a[i] = mul(a[i - 1], inv[i]);
	a[0] = 0; return a;
}
poly ln(poly a, int len){
	a = integ(deriv(a) * Inv(a, len));
	a.resize(len); return a;
}
poly exp(poly a, int len){
	int n = a.size(); poly c, b(1, 1);
	for(int i = 2; (i >> 1)	< len; i <<= 1){
		c = ln(b, i); c[0] = add(c[0], Mod-1);
		for(int j = 0; j < i; j++) c[j] = add(j < n ? a[j] : 0, Mod - c[j]);
		b = b * c; b.resize(i);
	} b.resize(len); return b;
}
int main(){
	prework();
	n = read();
	k = read();
	for(int i = 1; i <= k; i++) a[i] = read();
	L = read();
	R = read();
	f[1].resize(n + 1); f[1][1] = 1; f[0].resize(n + 1);
	for(int i = 2; i <= n; i++){
		f[i] = integ(exp(f[i - 1], n + 1)); f[i].resize(n + 1);
		for(int j = 1; j <= k; j++) f[i][a[j]] = 0;
	} int fac = 1;
	for(int i = 1; i <= n; i++) fac = mul(fac, i);
	for(int i = L; i <= R; i++) cout << mul(fac, add(f[i][n], Mod - f[i - 1][n]))<< " ";	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值