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;
}