UOJ Test Round #2 社论

比赛链接 .

模拟赛 C, D 出的 UTR 题,,,

开始复读官方题解 /oh/oh/oh 魔怔码风见谅 QwQ

A. 题目排列顺序

给一个序列 {fn},重排标准排列 π,使得 π[1:i] 的 LIS 长度恰为 fi .

n105 .

按权值第一关键字升序,下标第二关键字降序排序分配 1n 即可 .

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,问边权分配方案数 .

n400k109,对 998244353 取模 .

首先是无解情况(i,j,k):

  1. di,j>k .
  2. di,i0 .
  3. di,jdj,i .
  4. di,j>di,k+dk,j .

du,v>0 的情况,我们只需要对于每条边 (u,v),判断是否存在一个点 k 满足 du,v=du,k+dk,v,如果不存在这样的 k,那么这条边的权值就只能是 du,v,否则这条边只要大于等于 du,v 就可以了,即有 kdu,v+1 种方案,把所有边的方案乘起来就可以了 .

du,v 可能等于 0,则缩起来所有 du,v=0 的边构成的团,于是贡献分两部分:

  • 团间:等价于有重边的 du,v>0 情况,设是 a 重边,则显然如果存在 k 使得 du,v=du,k+dk,v,有 (kdu,v+1)a 种方案,若不然则是 (kdu,v+1)a(kdu,v)a 种 .
  • 团内,考虑容斥,令 fin 个点的距离为 0 的团的方案数,gin 个点的图的方案数,则

    gn=(k+1)(n2)fn=gni=1n1(n1i1)figniki(ni)

爆算乘起来即可,O(n3) .

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. 题目难度提升

给一个序列 {an},重排使得前缀中位数单调不减,求重排后字典序最大的 {a} .

n105,无解输出 QwQ .

考虑没有相同的数的情况,注意到若目前中位数为 m,加一个 a 进去,则如果 a<m 则中位数减小,a>m 则中位数增加 .

于是按位枚举,一个前缀有解当且仅当前缀的中位数 m 小于等于所有还没有放的数 .

最开始先放最小的数,每一阶段放两个数进去 . 阶段开始时因为有奇数个数,所以中位数一定落在某个数上 .

假设开始时中位数是 m,大于 m 的第一个还没有放的数是 k,如果 (m,k) 中已经有数放了,那么这个数不管放什么都可以,直接放最大的数就可以了 . 否则新放入的数 a 一定要满足 a+m2k,即 a2km,令 R=2km,则如果 (m,R] 中已经有数放了,直接放最大的数就可以了,否则找到 (m,R] 中最大的数放进去即可 .

假设此时中位数是 M,大于等于 M 的第一个还没有放的数是 L,如果 (m,L) 中已经有数放了,那么直接放最大的数,否则就只能放 L 了 .

这样就没了,有相同的数的情况就令所有数的中位数是 m,如果小于等于中位数的数中没有重复出现的数字,那么算法一仍然适用,否则令 R 为第一个小于等于 m 的重复出现的数字,然后整一个类似 two-pointers 的东西放 R,按字典序一次放两个大概就行了 .

时间复杂度大概 O(nlogn) 吧 .

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;
}
posted @   yspm  阅读(63)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
😅​
点击右上角即可分享
微信分享提示