省选模拟 20/02/02

Contest 传送门


T 1 : T1:
考虑直接贪心,每次把最优的两个顺着往后放,需要保证选出来的两个合法
也就是第一个数在它所在的段的奇数位,第二个数与第一个数在不同位
选出来过后有会多两个断点分成 3 段
然后就是在所有合法位置中选一个最小的再删除
我的沙雕做法:
考虑到增加断点过后会有奇数段变成偶数段,于是乎建两棵 s p l a y splay ,互换子树维护集合最小值即可
其实正解就是用个对维护每个段,ST 表维护奇数偶数的最小值
W a l l Wall 报错管都没管挂成 10 分。。。

#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
#define mp make_pair
cs int N = 2e5 + 50;
cs int INF = 1e9;
int n, a[N], ps[N], lg[N];
multiset<int> B;
typedef multiset<int>::iterator It;
struct ST{
	int a[N][20];
	ST(){ memset(a, 0x3f, sizeof(a)); }
	void build(){
		for(int j = 1; (1 << j) <= n; ++j)
		for(int i = 1; i + (1<<j) - 1 <= n; ++i)
		a[i][j] = min(a[i][j-1], a[i+(1<<j-1)][j-1]);
	}
	int query(int l, int r){
		if(l > r) return INF; int x = lg[r-l+1];
		return min(a[l][x],a[r-(1<<x)+1][x]);
	}
}M[2];
namespace Splay{
	cs int N = ::N<<1;
	bool FLAG; int rt[2],nd;
	int ch[N][2],fa[N],vl[N],mi[N],sz[N];
	void init(){ mi[0] = vl[0] = INF; }
	void pushup(int x){
		sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + 1;
		mi[x] = min(vl[x], min(mi[ch[x][0]],mi[ch[x][1]]));
	}
	int build(int anc, int l, int r){
		if(l > r) return 0;
		int mid = (l+r) >> 1, x = ++nd; fa[x] = anc;
		if(mid & 1 ^ FLAG) vl[x] = a[mid];
		else vl[x] = INF;
		ch[x][0] = build(x, l, mid-1);
		ch[x][1] = build(x, mid+1, r);
		pushup(x); return x;
	}
	int get(int x){ return ch[fa[x]][1] == x; }
	void rot(int x){
		int y = fa[x], z = fa[y], k = get(x);
		ch[z][get(y)] = x; fa[x] = z; 
		ch[y][k] = ch[x][k^1]; fa[ch[x][k^1]] = y;
		ch[x][k^1] = y; fa[y] = x; pushup(y); pushup(x);
	}
	void splay(int x, int gl, int &rt){
		while(fa[x] ^ gl){
			int y = fa[x]; 
			if(fa[y] ^ gl) rot(get(x) ^ get(y) ? x : y); rot(x);
		} if(gl == 0) rt = x;
	}
	int find(int x, int k){
		if(sz[ch[x][0]] >= k) return find(ch[x][0], k);
		else if(sz[ch[x][0]] + 1 == k) return x;
		return find(ch[x][1], k - sz[ch[x][0]] - 1); 
	}
	void ban(int k){ 
		int x = find(rt[0], k + 1); splay(x, 0, rt[0]); 
		vl[x] = INF; pushup(x); 
	}
	void Swap(int l, int r){
		if(l > r) return;
		int x1 = find(rt[0], l), y1 = find(rt[0], r + 2);
		splay(x1, 0, rt[0]); splay(y1, rt[0], rt[1]);
		int x2 = find(rt[1], l), y2 = find(rt[1], r + 2);
		splay(x2, 0, rt[1]); splay(y2, rt[1], rt[1]);
		int p1 = ch[y1][0], p2 = ch[y2][0];
		ch[y1][0] = p2; fa[p2] = y1; pushup(y1); pushup(x1);
		ch[y2][0] = p1; fa[p1] = y2; pushup(y2); pushup(x2);
	}
}
int main(){
	n = read();
	bool Subtask = true;
	for(int i = 1; i <= n; i++){
		a[i] = read(); ps[a[i]] = i;
		M[i & 1].a[i][0] = a[i];
		if(a[i] ^ i) Subtask = false;
	}
	if(Subtask){
		for(int i = 1; i <= n; i++) cout << a[i] << " ";
		return 0;
	}
	B.insert(0); B.insert(n + 1);
	for(int i = 2; i <= n; i++) lg[i] = lg[i>>1] + 1;
	M[0].build();
	M[1].build();
	Splay::init(); a[0] = a[n+1] = INF;
	Splay::FLAG = 0; Splay::rt[0] = Splay::build(0,0,n+1);
	Splay::FLAG = 1; Splay::rt[1] = Splay::build(0,0,n+1);
	for(int T = 1, up = n/2; T <= up; T++){
		int pl = ps[Splay::mi[Splay::rt[0]]];
		It it = B.lower_bound(pl); int nxt = *it;
		int pr = ps[M[pl & 1 ^ 1].query(pl + 1, nxt - 1)];
		cout << a[pl] << " " << a[pr] << " ";
		Splay::ban(pl); Splay::ban(pr);
		B.insert(pl); B.insert(pr);
		Splay::Swap(pl + 1, pr - 1);
	} return 0;
}
#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
#define mp make_pair
cs int N = 2e5 + 50;
cs int INF = 1e9;
int n, a[N], ps[N], lg[N];
struct ST{
	int a[N][20];
	ST(){ memset(a, 0x3f, sizeof(a)); }
	void build(){
		for(int j = 1; (1 << j) <= n; ++j)
		for(int i = 1; i + (1<<j) - 1 <= n; ++i)
		a[i][j] = min(a[i][j-1], a[i+(1<<j-1)][j-1]);
	}
	int query(int l, int r){
		if(l > r) return INF; int x = lg[r-l+1];
		return min(a[l][x],a[r-(1<<x)+1][x]);
	}
}M[2];
struct node{
	int l, r, v;
	bool operator < (cs node &a) cs{ return v > a.v; }
};
priority_queue<node> S;
int qry(int l, int r){ return M[l&1].query(l, r); }
int main(){
	n = read();
	for(int i = 1; i <= n; i++){
		a[i] = read(); ps[a[i]] = i;
		M[i & 1].a[i][0] = a[i];
	}
	for(int i = 2; i <= n; i++) lg[i] = lg[i>>1] + 1;
	M[0].build();
	M[1].build();
	S.push((node){1, n, qry(1,n)}); 
	for(int T = 1, up = n/2; T <= up; T++){
		node now = S.top(); S.pop();
		int pl = ps[now.v];
		int pr = ps[M[pl & 1 ^ 1].query(pl + 1, now.r)];
		cout << a[pl] << " " << a[pr] << " ";
		if(pl-1 >= now.l) S.push((node){now.l,pl-1,qry(now.l,pl-1)});
		if(pl+1 <= pr-1) S.push((node){pl+1,pr-1,qry(pl+1,pr-1)});
		if(pr+1 <= now.r) S.push((node){pr+1,now.r,qry(pr+1,now.r)});
	} return 0;
}

T 2 : T2:
不是很难的数位 d p dp 只不过考场没有仔细想
预处理前 i 位数的个数,数拼起来的长度,某个数出现的次数
然后单点查的时候可以先得到长度然后得到是什么数
查前缀和可以先把 < 当前位数的加上,然后统计当前位数的
统计当前位数的一个比较好的方法是看每一位出现了多少次

#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
cs int N = 20;
typedef long long ll;
cs ll INF = 1e18;
int T; ll len[N], deg[N], sm[16][N];
char out[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
void prework(){
	deg[0] = 1;
	for(int i = 1; deg[i-1] < INF; i++){
		deg[i] = deg[i - 1] * 15;
		len[i] = deg[i] * i + len[i - 1];
		sm[0][i] = deg[i] / 16 * (i - 1);
		for(int c = 1; c < 16; c++)
		sm[c][i] = deg[i] / 15 + deg[i] / 16 * (i - 1);
		deg[i] += deg[i - 1];
	}
	for(int i = 0; deg[i-1] < INF; i++) --deg[i];
}
int query(ll k){
	int m = 0;
	for(m = 0; len[m] <= k; m++);
	k -= len[m - 1];
	ll x = deg[m - 1] + k / m;
	k -= k / m * m;
	if(!k) return x & 15;
	++x; return (x >> (m - k << 2)) & 15;
}
ll ask(int d, ll x, int c){
	ll ans = 0;
	for(int i = 1; i < d; i++) ans += sm[c][i];
	ll v = x - deg[d - 1], res = 0;
	for(int i = 1; i < d; i++){ 
		ans += (ll)(v / 16 + (v % 16 > c)) << (i - 1 << 2);
		if(v % 16 == c) ans += res;
		res += (ll)(v % 16) << (i - 1 << 2);
		v /= 16;
	} 
	if(c){
		ans += (ll)(v % 16 >= c) << (d - 1 << 2);
		if(v % 16 == c - 1) ans += res;
	} return ans;
}
ll qrysum(ll k, int c){
	int m = 0;
	for(m = 0; len[m] <= k; m++);
	k -= len[m - 1];
	ll x = deg[m - 1] + k / m;
	k -= k / m * m;
	ll ans = ask(m, x, c); 
	for(ll i = 1; i <= k; i++){
		int t = ((x + 1) >> (m - i << 2)) & 15;
		if(t == c) ++ans;
	} return ans;
}
int main(){
	prework();
	T = read(); char ch[3]; 
	while(T--){
		int op = read(); ll x; scanf("%lld", &x);
		if(op == 1) cout << out[query(x)] << '\n';
		else{
			scanf("%s", ch); int c = 0;
			if(isdigit(ch[0])) c = ch[0] - '0';
			else c = ch[0] - 'A' + 10;
			cout << qrysum(x,c) << '\n';
		}
	} return 0;
}

T 3 : T3:
挺有意思的数数题
考虑 d p dp ,令 f i , j f_{i,j} 为到第 i i 列,有 j j 行 (这 j j 行都若干个黑色)
我们新增一列,这一列要么是自己列的本质不同列,要么是新增行的本质不同行
否则就是重复的
枚举新增不同行转移,如果没有新增,那么一定是本质不同列,一列可以有 0 , 1 , 2 0,1,2
f i , j ( 1 + j + ( j 2 ) )   > f i + 1 , j f_{i,j}*(1+j+\binom{j}{2})\ ->f_{i+1,j}
如果有新增本质不同行,假设有 k k 个,把 k k 个放进去,在这 k k 个的最上方或是最下方还可能有黑格因为对本质不同列有影响
假设最上方的格子行是 l l ,最下方是 r r ,那么这种方案要有 l ( k + j + 1 r ) l*(k+j+1-r) 的贡献
发现就是添加两个特殊行进去排列(喜闻乐见的套路),方案数 ( j + k + 2 k + 2 ) \binom{j+k+2}{k+2}
f i , j ( j + k + 2 k + 2 )   > f i + 1 , j + k f_{i,j}*\binom{j+k+2}{k+2}\ ->f_{i+1,j+k}
N T T NTT 优化转移, O ( m n l o g ( n ) ) O(mnlog(n))

#include<bits/stdc++.h>
#define cs const
using namespace std;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
cs int Mod = 998244353;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int dec(int a, int b){ return a - b < 0 ? a - b + Mod : a - b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
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; }
void Add(int &a, int b){ a = add(a, b); }
void Mul(int &a, int b){ a = mul(a, b); }
cs int N = 8050;
#define poly vector<int>
int n, m, fac[N], ifac[N];
int Binom(int n, int m){ return mul(fac[n],mul(ifac[n-m],ifac[m])); }
void prework(int n){
	fac[0] = fac[1] = ifac[0] = ifac[1] = 1;
	for(int i = 2; i <= n; i++) fac[i] = mul(fac[i-1], i);
	ifac[n] = ksm(fac[n], Mod-2);
	for(int i = n-1; i >= 2; i--) ifac[i] = mul(ifac[i+1], i+1);
}
cs int C = 14;
poly w[C+1];
void NTT_init(){
	for(int i = 1; i <= C; i++) w[i].resize(1<<i-1);
	int wn = ksm(3,(Mod-1)/(1<<C)); w[C][0] = 1;
	for(int i = 1; i < (1<<C-1); i++) w[C][i] = mul(w[C][i-1],wn);
	for(int i = C-1; i; i--) for(int j = 0; j < (1<<i-1); j++) w[i][j]=w[i+1][j<<1];
}
int bit, up; poly rev;
void init(int deg){
	bit = 0, up = 1; while(up < deg) up <<= 1, ++bit; rev.resize(up); 
	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] = dec(x,y);
	}
	if(typ == -1){
		reverse(a.begin()+1,a.end());
		for(int i=0,iv=ksm(up,Mod-2); i<up; i++) Mul(a[i],iv); 
	}
}
int main(){
	n = read(), m = read(); prework(n + 5);
	NTT_init(); init(n + n + 2);
	poly f; f.push_back(1);
	poly g(n+1, 0);
	for(int i = 1; i <= n; i++) g[i] = ifac[i + 2];
	g.resize(up); NTT(g, 1);
	for(int j = 1; j <= m; j++){
		poly h = f; h.resize(up);  
		for(int i = 0; i <= n; i++) Mul(h[i],ifac[i]);
		NTT(h, 1);
		for(int i = 0; i < up; i++) Mul(h[i],g[i]);
		NTT(h, -1); h.resize(n+1); f.resize(n+1);
		for(int i = 0; i <= n; i++) 
		f[i] = add(mul(h[i],fac[i+2]), mul(i*(i+1)/2+1,f[i]));
	}
	int ans = 0;
	for(int i = 0; i <= n; i++) 
	Add(ans, mul(f[i], Binom(n,i)));
	cout << ans; return 0;
}
发布了634 篇原创文章 · 获赞 98 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/sslz_fsy/article/details/104163701