2022NOIP A层联测25

A. 惊喜二十二

如果把大于/小于关系看成有向边,那么我们要构造没有环的方案

先把 \(a\) 升序排列, \(b\)对应移动,这样显然不影响答案

于是对于 \(i < j\)\(a_i < a_j\)

那么如果 \(b_i < a_i\)

\(b_i > b_j\)

那么 \(j\) 位置一定不能填 \(1\)

那么我们可以把限制转化成如果 \(i\)\(1\), 那么 \(i < j\) \(b_i > b_j\)\(j\) 一定为 \(1\)

于是我们就可以设计状态 \(f_{i, j} 表示\) 考虑前 \(i\) 个数,前面最大的填 \(1\) 的为 \(j\)的方案数

转移考虑填 \(1 / 0\)

对于 \(b\) 不确定的我们考虑加上一维

\(f_{i, j, k}\) \(k\)表示不确定的我们确定了多少位,这些位是在 \(DP\) 过程中更新过 \(j\)

对于没确定的,我们最后乘上组合数即可

code
#include<bits/stdc++.h>


using namespace std;

typedef long long ll;
typedef unsigned long long ull;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}

const int maxn = 105;
const int mod = 998244353;
int n, a[maxn], b[maxn], vis[maxn], fac[maxn];
int f[maxn][maxn][maxn];
void add(int &x, int y){x += y; x = x >= mod ? x - mod : x;}
int main(){
	freopen("surprise.in","r",stdin);
	freopen("surprise.out","w",stdout);
	n = read();
	for(int i = 1; i <= n; ++i)a[i] = read();
	for(int i = 1; i <= n; ++i)b[a[i]] = read();
	for(int i = 1; i <= n; ++i)vis[b[i]] = true;
	f[0][0][0] = 1;
	a[0] = 0; for(int i = 1; i <= n; ++i)a[i] = a[i - 1] + (!vis[i]);
	for(int i = 0; i < n; ++i)
		for(int j = 0; j <= n; ++j)
			for(int k = 0; k <= a[j]; ++k)if(f[i][j][k]){
				add(f[i + 1][j][k], f[i][j][k]);
				if(b[i + 1] > j)add(f[i + 1][b[i + 1]][k], f[i][j][k]);
				if(b[i + 1] == 0)for(int p = j + 1; p <= n; ++p)if(vis[p] == false)add(f[i + 1][p][k + 1], f[i][j][k]);
			}
	fac[0] = 1; for(int i = 1; i <= a[n]; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
	int ans = 0;
	for(int i = 0; i <= n; ++i)for(int j = 0; j <= a[n]; ++j)add(ans, 1ll * f[n][i][j] * fac[a[n] - j] % mod);
	printf("%d\n",ans);
	return 0;
}

B. K-构造

提交答案题,浪费了很多时间

手玩了一些东西发现了一些性质,但是没有及时总结,并且没有打代码的想法。

发现可以用若干 \(1 / -1\) 凑出一些初始的数,每次可以合并两个数(乘起来,一边乘上一个大常数防止影响),还可以通过加入所有负数和的绝对值使得答案 \(+1\),据此可以写出随机化

但是实测出解率很低,而且大概率用的数很多,可能是我写丑了

\(Eafoo\)的做法是随机正数序列,背包出凑成数的方案数,然后添上对应的负数得到方案,跑的很快

C. 函数的权力

数位 \(DP\) 转贪心

发现 \(F\)是把数转化为 \(K\) 进制,求所有数位的和 + 数的位数 - 2

那么根据上界走,当某一位不走上界了,那么贪心的让后面全是 \(k - 1\) 该位为 \(a_i - 1\) 一定最优

下界的限制决定了开头连续若干位必须走上界

\(ans2/ans3\)只需要记录不走上界的那个位置即可

code
#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

ll read(){
	ll x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}
const int maxn = 10005;
ll k, l, r, ans1, ans2, ans3;
int lenl, lenr;
ll base[maxn], sum[maxn], L[maxn], R[maxn];
void trans(ll x, ll a[], int &len){ len = -1; while(x){a[++len] = x % k; x /= k;} if(len == -1){a[++len] = 0;}}
void sol(){
	k = read(), l = read(), r = read();
	trans(l, L, lenl); trans(r, R, lenr);
	base[0] = 1; for(int i = 1; i <= lenr; ++i)base[i] = base[i - 1] * k;
	sum[0] = R[0]; for(int i = 1; i <= lenr; ++i)sum[i] = sum[i - 1] + R[i];
	ans1 = sum[lenr] + lenr - 1; ll p2 = -1, p3 = -1;
	int mx = lenr; 
	if(lenl == lenr)while(mx >= 0 && L[mx] == R[mx])--mx;
	for(int i = 1; i <= mx; ++i)if(R[i]){
		ll now = sum[lenr] - sum[i] + R[i] - 1 + i * (k - 1);
		now = now + lenr - 1 - (i == lenr && R[i] == 1);
		if(now > ans1)ans1 = now, p3 = i;
		if(now == ans1)p2 = i;
	}
	ans2 = ans3 = 0;
	for(int i = 0; i <= lenr; ++i){
		if(i == p2)ans2 += (R[i] - 1) * base[i];
		else if(i < p2)ans2 += (k - 1) * base[i];
		else ans2 += R[i] * base[i];
	}
	for(int i = 0; i <= lenr; ++i){
		if(i == p3)ans3 += (R[i] - 1) * base[i];
		else if(i < p3)ans3 += (k - 1) * base[i];
		else ans3 += R[i] * base[i];
	}
	printf("%lld %lld %lld\n",ans1, ans2, ans3);
}
int main(){
	freopen("powerf.in","r",stdin);
	freopen("powerf.out","w",stdout);
	int  t = read();
	for(int i = 1; i <= t; ++i)sol();
	return 0;
}

D. 最大可达流形

本场签到题,但是完全没思考

在每个点二分出能放的最大矩形

然后跑最大生成树找路径边权最小值

然后发现在克鲁斯卡尔重构树上找 \(LCA\) 即可

code
#include<bits/stdc++.h>


using namespace std;

typedef long long ll;
typedef unsigned long long ull;

int read(){
	int x = 0; char c = getchar();
	while(!isdigit(c))c = getchar();
	do{x = x * 10 + (c ^ 48); c = getchar();}while(isdigit(c));
	return x;
}

const int maxn = 1005;
char mp[maxn][maxn];
int sum[maxn][maxn];
int val[maxn][maxn], p[maxn * maxn * 2];
int get_sum(int l1, int r1, int l2, int r2){return sum[l2][r2] - sum[l1 - 1][r2] - sum[l2][r1 - 1] + sum[l1 - 1][r1 - 1];}
struct EDGE{
	int u, v, w;
	friend bool operator < (const EDGE &x, const EDGE &y){
		return x.w > y.w;
	}
}E[maxn * maxn * 4];
int n;
int f[maxn * maxn * 2];
int find(int x){return f[x] == x ? x : f[x] = find(f[x]);}

int head[maxn * maxn * 2], tot;
struct edge{int to, net;}e[maxn * maxn * 4];
int size[maxn * maxn * 2], son[maxn * maxn * 2], fa[maxn * maxn * 2], dep[maxn * maxn * 2], top[maxn * maxn * 2];
void add(int u, int v){
	e[++tot].net = head[u];
	head[u] = tot;
	e[tot].to = v;
}
void dfs1(int x){
	size[x] = 1;
	for(int i = head[x]; i; i = e[i].net){
		int v = e[i].to;
		if(v == fa[x])continue;
		dep[v] = dep[x] + 1; fa[v] = x;
		dfs1(v);
		size[x] += size[v]; 
		son[x] = size[son[x]] > size[v] ? son[x] : v;
	}
}
void dfs2(int x, int tp){
	top[x] = tp;
	if(son[x])dfs2(son[x], tp);
	for(int i = head[x]; i; i = e[i].net){
		int v = e[i].to;
		if(v == fa[x] || v == son[x])continue;
		dfs2(v, v);
	}
}
int lca(int u, int v){
	while(top[u] != top[v]){
		if(dep[top[u]] < dep[top[v]])swap(u, v);
		u = fa[top[u]];
	}
	return dep[u] < dep[v] ? u : v;
}
int id(int x, int y){return (x - 1) * n + y;}

int main(){
	freopen("planem.in","r",stdin);
	freopen("planem.out","w",stdout);
	n = read();
	for(int i = 1; i <= n; ++i)scanf("%s",mp[i] + 1);
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= n; ++j)
			sum[i][j] = (mp[i][j] == '#') + sum[i][j - 1] + sum[i - 1][j] - sum[i - 1][j - 1];
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= n; ++j)if(mp[i][j] != '#'){
			int l = 1, r = min({i, n - i + 1, j, n - j + 1}), ans = l;
			while(l <= r){
				int mid = (l + r) >> 1;
				if(get_sum(i - mid + 1, j - mid + 1, i + mid - 1, j + mid - 1))r = mid - 1;
				else l = mid + 1, ans = mid;
			}
			val[i][j] = ans + ans - 1;
		}
	int cnt = 0;
	for(int i = 2; i <= n; ++i)
		for(int j = 1; j <= n; ++j)if(mp[i][j] != '#' && mp[i - 1][j] != '#')E[++cnt] = {id(i - 1, j), id(i, j), min(val[i - 1][j], val[i][j])};
	for(int i = 1; i <= n; ++i)
		for(int j = 2; j <= n; ++j)if(mp[i][j] != '#' && mp[i][j - 1] != '#')E[++cnt] = {id(i, j - 1), id(i, j), min(val[i][j - 1], val[i][j])};
	sort(E + 1, E + cnt + 1);
	for(int i = 1; i <= n * n * 2; ++i)f[i] = i;
	int node = n * n;
	for(int i = 1; i <= cnt; ++i){
		int u = find(E[i].u), v = find(E[i].v);
		if(u == v)continue;
		++node; add(node, u); add(node, v);
		f[u] = f[v] = node; p[node] = E[i].w;
	}
	dfs1(node); dfs2(node, node);
	int q = read();
	for(int i = 1; i <= q; ++i){
		int ra = read(), ca = read(), rb = read(), cb = read();
		int ia = id(ra, ca), ib = id(rb, cb);
		if(ia == ib)printf("%d\n",val[ra][ca]);
		else printf("%d\n",p[lca(ia, ib)]);
	}
	return 0;
}

posted @ 2022-11-11 20:12  Chen_jr  阅读(21)  评论(0编辑  收藏  举报