UOJ Test Round #2 社论
比赛链接 .
模拟赛 C, D 出的 UTR 题,,,
开始复读官方题解 /oh/oh/oh 魔怔码风见谅 QwQ
A. 题目排列顺序
给一个序列 \(\{f_n\}\),重排标准排列 \(\pi\),使得 \(\pi[1:i]\) 的 LIS 长度恰为 \(f_i\) .
\(n\le 10^5\) .
按权值第一关键字升序,下标第二关键字降序排序分配 \(1\dots n\) 即可 .
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. 题目交流通道
无向完全图 \(G(V,E)\) 给定任意两点间最短路,每条边边权不大于 \(k\),问边权分配方案数 .
\(n\le 400\),\(k\le 10^9\),对 \(998244353\) 取模 .
首先是无解情况(\(\exists i,j,k\)):
- \(d_{i,j}>k\) .
- \(d_{i,i}\neq 0\) .
- \(d_{i,j}\neq d_{j,i}\) .
- \(d_{i,j}>d_{i,k}+d_{k,j}\) .
\(d_{u,v}>0\) 的情况,我们只需要对于每条边 \((u,v)\),判断是否存在一个点 \(k\) 满足 \(d_{u,v}=d_{u,k}+d_{k,v}\),如果不存在这样的 \(k\),那么这条边的权值就只能是 \(d_{u,v}\),否则这条边只要大于等于 \(d_{u,v}\) 就可以了,即有 \(k−d_{u,v}+1\) 种方案,把所有边的方案乘起来就可以了 .
若 \(d_{u,v}\) 可能等于 \(0\),则缩起来所有 \(d_{u,v}=0\) 的边构成的团,于是贡献分两部分:
- 团间:等价于有重边的 \(d_{u,v}>0\) 情况,设是 \(a\) 重边,则显然如果存在 \(k\) 使得 \(d_{u,v}=d_{u,k}+d_{k,v}\),有 \((k-d_{u,v}+1)^a\) 种方案,若不然则是 \((k-d_{u,v}+1)^a-(k-d_{u,v})^a\) 种 .
- 团内,考虑容斥,令 \(f_i\) 为 \(n\) 个点的距离为 \(0\) 的团的方案数,\(g_i\) 为 \(n\) 个点的图的方案数,则\[\begin{aligned}g_n&=(k+1)^{\binom n2}\\\displaystyle f_n&=g_n-\sum_{i=1}^{n-1}\dbinom{n-1}{i-1}f_ig_{n-i}\cdot k^{i(n-i)}\end{aligned} \]
爆算乘起来即可,\(O(n^3)\) .
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. 题目难度提升
给一个序列 \(\{a_n\}\),重排使得前缀中位数单调不减,求重排后字典序最大的 \(\{a\}\) .
\(n\le 10^5\),无解输出
QwQ
.
考虑没有相同的数的情况,注意到若目前中位数为 \(m\),加一个 \(a\) 进去,则如果 \(a<m\) 则中位数减小,\(a>m\) 则中位数增加 .
于是按位枚举,一个前缀有解当且仅当前缀的中位数 \(m\) 小于等于所有还没有放的数 .
最开始先放最小的数,每一阶段放两个数进去 . 阶段开始时因为有奇数个数,所以中位数一定落在某个数上 .
假设开始时中位数是 \(m\),大于 \(m\) 的第一个还没有放的数是 \(k\),如果 \((m,k)\) 中已经有数放了,那么这个数不管放什么都可以,直接放最大的数就可以了 . 否则新放入的数 \(a\) 一定要满足 \(a+m\le 2k\),即 \(a\le 2k−m\),令 \(R=2k−m\),则如果 \((m,R]\) 中已经有数放了,直接放最大的数就可以了,否则找到 \((m,R]\) 中最大的数放进去即可 .
假设此时中位数是 \(M\),大于等于 \(M\) 的第一个还没有放的数是 \(L\),如果 \((m,L)\) 中已经有数放了,那么直接放最大的数,否则就只能放 \(L\) 了 .
这样就没了,有相同的数的情况就令所有数的中位数是 \(m\),如果小于等于中位数的数中没有重复出现的数字,那么算法一仍然适用,否则令 \(R\) 为第一个小于等于 \(m\) 的重复出现的数字,然后整一个类似 two-pointers 的东西放 \(R\),按字典序一次放两个大概就行了 .
时间复杂度大概 \(O(n\log n)\) 吧 .
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;
}
以下是博客签名,正文无关
本文来自博客园,作者:Jijidawang,转载请注明原文链接:https://www.cnblogs.com/CDOI-24374/p/16555616.html
版权声明:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0)进行许可。看完如果觉得有用请点个赞吧 QwQ