UOJ Test Round #2 社论
比赛链接 .
模拟赛 C, D 出的 UTR 题,,,
开始复读官方题解 /oh/oh/oh 魔怔码风见谅 QwQ
A. 题目排列顺序
给一个序列 ,重排标准排列 ,使得 的 LIS 长度恰为 .
.
按权值第一关键字升序,下标第二关键字降序排序分配 即可 .
Code
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int, int> pii;
const int N = 1e5 + 7;
int n, ans[N];
pii a[N];
int main()
{
scanf("%d", &n);
for (int i=1, x; i<=n; i++){scanf("%d", &x); a[i] = make_pair(x, -i);}
stable_sort(a+1, a+1+n);
for (int i=1; i<=n; i++) ans[-a[i].second] = i;
for (int i=1; i<=n; i++) printf("%d ", ans[i]);
puts("");
return 0;
}
B. 题目交流通道
无向完全图 给定任意两点间最短路,每条边边权不大于 ,问边权分配方案数 .
,,对 取模 .
首先是无解情况():
- .
- .
- .
- .
的情况,我们只需要对于每条边 ,判断是否存在一个点 满足 ,如果不存在这样的 ,那么这条边的权值就只能是 ,否则这条边只要大于等于 就可以了,即有 种方案,把所有边的方案乘起来就可以了 .
若 可能等于 ,则缩起来所有 的边构成的团,于是贡献分两部分:
- 团间:等价于有重边的 情况,设是 重边,则显然如果存在 使得 ,有 种方案,若不然则是 种 .
- 团内,考虑容斥,令 为 个点的距离为 的团的方案数, 为 个点的图的方案数,则
爆算乘起来即可, .
Code
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<ll, ll> pll;
const int N = 444, P = 998244353;
int n, k;
ll d[N][N], v[N][N], cc[N][N], bel[N], siz[N], f[N], g[N], ans = 1;
bool ok[N][N];
struct dsu
{
int fa[N];
dsu(){iota(fa, fa+N, 0);}
int get(int x){return x == fa[x] ? x : fa[x] = get(fa[x]);}
inline void merge(int u, int v){fa[get(u)] = get(v);}
}D;
inline int qpow(int a, int n)
{
int ans = 1;
while (n)
{
if (n & 1) ans = 1ll * ans * a % P;
a = 1ll * a * a % P; n >>= 1;
} return ans;
}
inline int inv(int x){return qpow(x, P-2);}
inline bool check()
{
for (int i=1; i<=n; i++)
for (int j=1; j<=n; j++)
{
if ((i == j) && d[i][j]) return false;
if (d[i][j] > k) return false;
if (d[i][j] != d[j][i]) return false;
for (int k=1; k<=n; k++)
if ((i != j) && (i != k) && (d[i][j] > d[i][k] + d[k][j])) return false;
}
return true;
}
int fac[N], ifac[N];
inline int initf()
{
fac[0] = 1;
for (int i=1; i<N; i++) fac[i] = 1ll * fac[i-1] * i % P;
ifac[N-1] = inv(fac[N-1]);
for (int i=N-2; i>=0; i--) ifac[i] = 1ll * ifac[i+1] * (i+1) % P;
return 0xDEADC0DE;
} int _____________ = initf();
inline int binom(int n, int m){return n < m ? 0 : 1ll * fac[n] * ifac[m] % P * ifac[n-m] % P;}
int main()
{
scanf("%d%d", &n, &k);
for (int i=1; i<=n; i++)
for (int j=1; j<=n; j++) scanf("%lld", d[i] + j);
if (!check()){puts("0"); return 0;}
for (int i=2; i<=n; i++)
for (int j=1; j<i; j++)
if (!d[i][j] && (i != j)) D.merge(i, j);
for (int i=1; i<=n; i++){bel[i] = D.get(i); ++siz[bel[i]];}
for (int i=2; i<=n; i++)
for (int j=1; j<i; j++)
{
int bi = bel[i], bj = bel[j];
if (bi == bj) continue;
if (v[bi][bj] && (v[bi][bj] != d[i][j])){puts("0"); return 0;}
v[bi][bj] = v[bj][bi] = d[i][j]; ++cc[bi][bj]; ++cc[bj][bi];
}
for (int k=1; k<=n; k++)
for (int i=1; i<=n; i++)
for (int j=1; j<=n; j++)
ok[i][j] |= ((i != k) && (i != j) && (j != k) && siz[i] && siz[j] && siz[k] && (v[i][j] == v[i][k] + v[k][j]));
for (int i=2; i<=n; i++)
for (int j=1; j<i; j++)
if (siz[i] && siz[j])
{
if (ok[i][j]) ans = 1ll * ans * qpow(k - v[i][j] + 1, cc[i][j]) % P;
else ans = 1ll * ans * ((qpow(k - v[i][j] + 1, cc[i][j]) - qpow(k - v[i][j], cc[i][j])) % P + P) % P;
}
f[0] = g[0] = 1;
for (int i=1; i<=n; i++) g[i] = qpow(k + 1, i * (i - 1) >> 1);
for (int i=1; i<=n; i++)
{
f[i] = g[i];
for (int j=1; j<i; j++)
f[i] = (f[i] - 1ll * f[j] * g[i-j] % P * binom(i-1, j-1) % P * qpow(k, j * (i-j)) % P + P) % P;
}
for (int i=1; i<=n; i++) ans = 1ll * ans * f[siz[i]] % P;
printf("%lld\n", ans);
return 0;
}
C. 题目难度提升
给一个序列 ,重排使得前缀中位数单调不减,求重排后字典序最大的 .
,无解输出
QwQ
.
考虑没有相同的数的情况,注意到若目前中位数为 ,加一个 进去,则如果 则中位数减小, 则中位数增加 .
于是按位枚举,一个前缀有解当且仅当前缀的中位数 小于等于所有还没有放的数 .
最开始先放最小的数,每一阶段放两个数进去 . 阶段开始时因为有奇数个数,所以中位数一定落在某个数上 .
假设开始时中位数是 ,大于 的第一个还没有放的数是 ,如果 中已经有数放了,那么这个数不管放什么都可以,直接放最大的数就可以了 . 否则新放入的数 一定要满足 ,即 ,令 ,则如果 中已经有数放了,直接放最大的数就可以了,否则找到 中最大的数放进去即可 .
假设此时中位数是 ,大于等于 的第一个还没有放的数是 ,如果 中已经有数放了,那么直接放最大的数,否则就只能放 了 .
这样就没了,有相同的数的情况就令所有数的中位数是 ,如果小于等于中位数的数中没有重复出现的数字,那么算法一仍然适用,否则令 为第一个小于等于 的重复出现的数字,然后整一个类似 two-pointers 的东西放 ,按字典序一次放两个大概就行了 .
时间复杂度大概 吧 .
Code
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<ll, ll> pll;
const int N = 114514;
multiset<int> S;
struct
{
priority_queue<int, vector<int>, less<int> > L;
priority_queue<int, vector<int>, greater<int> > G;
inline void push(int val)
{
if (L.empty() || (val <= L.top())) L.push(val);
else G.push(val);
if (L.size() < G.size()){L.push(G.top()); G.pop();}
if (L.size() - G.size() > 1){G.push(L.top()); L.pop();}
}
inline int le(){return L.top();}
inline int gr(){return G.top();}
inline bool even(){return L.size() == G.size();}
inline bool existG(){return G.size();}
}T;
int n, a[N], cc;
bool vis[N];
int main()
{
scanf("%d", &n);
for (int i=1; i<=n; i++) scanf("%d", a+i);
stable_sort(a+1, a+1+n);
int ptr = (n + 1) >> 1;
if (a[ptr] == a[ptr+1])
{
while (a[ptr] == a[ptr+1]) ++ptr;
printf("%d ", a[ptr]);
int l = ptr-1, r = n;
while (l || (r > ptr))
{
if (l) printf("%d ", a[l--]);
if (r > ptr) printf("%d ", a[r--]);
}
return 0;
}
while ((ptr > 1) && (a[ptr] != a[ptr-1])) --ptr;
printf("%d ", a[ptr]); vis[ptr] = true;
int l = ptr-1, r = n;
while (l && (r > ptr))
{
printf("%d ", a[l]); vis[l--] = true;
printf("%d ", a[r]); vis[r--] = true;
}
for (int i=1; i<=n; i++)
if (vis[i]) T.push(a[i]);
else S.insert(a[i]);
while (!S.empty())
{
l = *S.begin();
printf("%d ", r = ((T.even()) ? ((l >= T.gr()) ? *(--S.end()) : *S.begin()) : ((T.existG() && ((l << 1) >= T.le() + T.gr())) ? *(--S.end()) : *(--S.upper_bound((l<<1) - T.le())))));
S.erase(S.find(r)); T.push(r);
}
return 0;
}
以下是博客签名,正文无关
本文来自博客园,作者:yspm,转载请注明原文链接:https://www.cnblogs.com/CDOI-24374/p/16555616.html
版权声明:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0)进行许可。看完如果觉得有用请点个赞吧 QwQ
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】