首页 > 代码库 > 【UTR #2】[UOJ#278]题目排列顺序 [UOJ#279]题目交流通道 [UOJ#280]题目难度提升

【UTR #2】[UOJ#278]题目排列顺序 [UOJ#279]题目交流通道 [UOJ#280]题目难度提升

[UOJ#278]【UTR #2】题目排列顺序

试题描述

“又要出题了。” 宇宙出题中心主任 —— 吉米多出题斯基,坐在办公桌前策划即将到来的 UOI。

这场比赛有 n 道题,吉米多出题斯基需要决定这些题目的难度,然后再在汪洋大海中寻找符合该难度的题目。

题目的难度可以用一个 1 到 n 的排列 a1,,an 表示,其中 ai 表示第 i 道题目在这 n 道题目中是第 ai 简单的题目,即恰有 ai?1 道题目比第 i 道题目简单。

经验丰富的吉米多出题斯基早就悟出了一种科学地决定难度顺序的方法。首先,吉米多出题斯基定义了难度递增子序列为序列 ap1,ap2,,apk1p1<p2<?<pkn) 满足 ap1<ap2<?<apk。然后,吉米多出题斯基决定了 n 个整数 f1,,fn,他希望找出一个难度顺序使得对于每个 1in 均满足以 ai 结尾的难度递增子序列的最长长度恰好为 fi

但吉米多出题斯基日理万机,于是他找到了你 —— 风璃殇做不出题耶维奇,请你帮助吉米多出题斯基构造一个符合条件的 a1,,an 吧!

输入

第一行一个正整数 n

接下来一行 n 个数,其中第 i 个数表示 fi。(1fin

输出

输出一行 n 个数,表示 a1,,an

题目保证至少存在一组解。如有多组解,输出任意一组均可。

输入示例

7
1 2 3 2 4 4 3

输出示例

1 4 5 2 7 6 3

数据规模及约定

n ≤ 105

题解

考虑最大数字的位置,可以是 fi 中最靠前的最大数的位置,在那个位置上填一个最大数,然后删掉那个位置的 fi,就好了。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
#include <map>
#include <set>
using namespace std;

const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {
    if(Head == Tail) {
        int l = fread(buffer, 1, BufferSize, stdin);
        Tail = (Head = buffer) + l;
    }
    return *Head++;
}
int read() {
    int x = 0, f = 1; char c = Getchar();
    while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = Getchar(); }
    while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = Getchar(); }
    return x * f;
}

#define maxn 100010
int A[maxn], B[maxn], hd[maxn], nxt[maxn];

int main() {
	int n = read();
	for(int i = 1; i <= n; i++) A[i] = read();
	
	for(int i = n; i; i--)
		nxt[i] = hd[A[i]], hd[A[i]] = i;
	int ToT = n;
	for(int i = n; i; i--)
		while(hd[i]) B[hd[i]] = ToT--, hd[i] = nxt[hd[i]];
	
	for(int i = 1; i <= n; i++) printf("%d%c", B[i], i < n ? ‘ ‘ : ‘\n‘);
	
	return 0;
}

比赛时手残把 checker 写错了,于是强行 YY 出了拓扑排序的做法,其实一个道理。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
#include <map>
#include <set>
using namespace std;

const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {
    if(Head == Tail) {
        int l = fread(buffer, 1, BufferSize, stdin);
        Tail = (Head = buffer) + l;
    }
    return *Head++;
}
int read() {
    int x = 0, f = 1; char c = Getchar();
    while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = Getchar(); }
    while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = Getchar(); }
    return x * f;
}

#define maxn 100010
#define maxm 200010
int lst[maxn], in[maxn], nxt[maxn], B[maxn], S[maxn];

int m, head[maxn], next[maxm], to[maxm];
void Add(int a, int b) {
	to[++m] = b; next[m] = head[a]; head[a] = m;
	in[b]++;
	return ;
}

int main() {
//	freopen("data.in", "r", stdin);
//	freopen("data.out", "w", stdout);
	int n = read();
	for(int i = 1; i <= n; i++) {
		int A = read();
//		printf("%d %d\n", lst[A-1], nxt[lst[A-1]]);
		Add(i, lst[A-1]);
		if(nxt[lst[A-1]]) Add(nxt[lst[A-1]], i);
		nxt[lst[A-1]] = i;
		if(!lst[A]) lst[A] = i;
	}
	
	int top = 0;
	for(int i = 1; i <= n; i++) if(!in[i]) S[++top] = i;
	int ToT = n;
	while(ToT--) {
		int u = S[top--];
		B[u] = ToT + 1;
		for(int e = head[u]; e; e = next[e]) {
			in[to[e]]--;
			if(!in[to[e]]) S[++top] = to[e];
		}
	}
	
	for(int i = 1; i <= n; i++) printf("%d%c", B[i], i < n ? ‘ ‘ : ‘\n‘);
	
	return 0;
}

 

试题描述

定好了难度,雄心勃勃的吉米多出题斯基开始寻找智慧的神犇星球的居民出题。

然而吉米多出题斯基没有料到,神犇星球的居民告诉吉米多出题斯基:“今年神犇星球经济不景气,大家都想宅在家里,哪有心思出来出题呢?”

为了挽救这一局面,吉米多出题斯基决定为神犇星球建一些高速传送通道促进该星球各地区之间交流题目。

神犇星球有 n 座小城。对于任意两座小城 v,uvu),吉米多出题斯基想在 v,u 之间建立一个传送时间为 w(v,u) 的无向传送通道,其中 w(v,u) 为不超过 k 的非负整数。建成后,神犇星球的居民可从一座小城出发经过一个或若干个传送通道到达另一座小城交流题目,花费的时间为所有经过的传送通道的传送时间之和。

吉米多出题斯基还没有决定每一个传送通道的传送时间取值,只是对于任意两座小城 v,u,决定了从 v 出发到达 u 的最短时间要恰好等于 d(v,u)。但吉米多出题斯基日理万机,于是他找到了你 —— 风璃殇做不出题耶维奇,请你帮助吉米多出题斯基数一数有多少种不同的满足条件的传送通道建设方案吧!

由于方案数可能很大,你只用输出方案数对 9982443537×17×223+1,一个质数)取模后的结果。

输入

第一行两个整数 n,k。保证 n1,k0

接下来 n 行,每行有 n 个非负整数,第 i 行的 第 j 个数表示 d(i,j) 的值。

输出

输出一行,一个整数,表示方案数对 998244353 取模的结果。如果无解,则方案数为 0

输入示例

3 5
0 3 4
3 0 1
4 1 0

输出示例

2

数据规模及约定

1n400,0k109

题解

UOJ 上题解挺好的,丢链接跑。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
#include <map>
#include <set>
using namespace std;

const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {
    if(Head == Tail) {
        int l = fread(buffer, 1, BufferSize, stdin);
        Tail = (Head = buffer) + l;
    }
    return *Head++;
}
int read() {
    int x = 0, f = 1; char c = Getchar();
    while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = Getchar(); }
    while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = Getchar(); }
    return x * f;
}

#define maxn 410
#define MOD 998244353
#define LL long long
int n, K, d[maxn][maxn], d2[maxn][maxn], ce[maxn][maxn], f[maxn], g[maxn], C[maxn][maxn];

bool check() {
	for(int i = 1; i <= n; i++) {
		if(d[i][i]) return 0;
		for(int j = i + 1; j <= n; j++) {
			if(d[i][j] != d[j][i] || d[i][j] > K) return 0;
			for(int k = 1; k <= n; k++)
				if(d[i][j] > d[i][k] + d[j][k]) return 0;
		}
	}
	return 1;
}

int Q[maxn], hd, tl, cn, No[maxn], siz[maxn];
void bfs(int s) {
	hd = tl = 0; Q[++tl] = s; No[s] = ++cn; siz[cn] = 1;
	while(hd < tl) {
		int u = Q[++hd];
		for(int v = 1; v <= n; v++)
			if(!No[v] && !d[u][v]) No[v] = cn, siz[cn]++, Q[++tl] = v;
	}
	return ;
}

LL Pow(int a, int b) {
	LL s = 1, t = a % MOD;
	while(b) {
		if(b & 1) (s *= t) %= MOD;
		(t *= t) %= MOD; b >>= 1;
	}
	return s;
}

int main() {
	n = read(); K = read();
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= n; j++) d[i][j] = read();
	if(!check()) return puts("0"), 0;
	
	for(int i = 1; i <= n; i++) if(!No[i]) bfs(i);
//	for(int i = 1; i <= n; i++) printf("%d%c", No[i], i < n ? ‘ ‘ : ‘\n‘);
	for(int i = 1; i <= n; i++)
		for(int j = i + 1; j <= n; j++) if(No[i] != No[j]) {
			d2[No[i]][No[j]] = d2[No[j]][No[i]] = d[i][j];
			ce[No[i]][No[j]]++; ce[No[j]][No[i]]++;
		}
	int ans = 1;
	for(int i = 1; i <= cn; i++)
		for(int j = i + 1; j <= cn; j++) {
			bool ok = 0;
			for(int k = 1; k <= cn; k++)
				if(k != i && k != j && d2[i][j] == d2[i][k] + d2[j][k]) {
					ok = 1; break;
				}
			LL tmp = Pow(K - d2[i][j] + 1, ce[i][j]);
			if(!ok) tmp -= Pow(K - d2[i][j], ce[i][j]);
			tmp = (tmp % MOD + MOD) % MOD;
			ans = ((LL)ans * tmp) % MOD;
		}
	for(int i = 0; i <= n; i++) {
		C[i][0] = C[i][i] = 1;
		for(int j = 1; j < i; j++) {
			C[i][j] = C[i-1][j] + C[i-1][j-1];
			if(C[i][j] >= MOD) C[i][j] -= MOD;
		}
	}
	g[1] = f[1] = 1;
	for(int i = 2; i <= n; i++) {
		f[i] = g[i] = Pow(K + 1, C[i][2]);
		for(int j = 1; j < i; j++) {
			f[i] -= (((LL)f[j] * g[i-j] % MOD) * C[i-1][j-1] % MOD) * Pow(K, j * (i - j)) % MOD;
			if(f[i] < 0) f[i] += MOD;
		}
	}
	for(int i = 1; i <= cn; i++) ans = (LL)ans * f[siz[i]] % MOD;
	printf("%d\n", ans);
	
	return 0;
}

 

试题描述

最终吉米多出题斯基没有找到优质的题目,于是高产的吉米多出题斯基决定自己出一道。

首先,吉米多出题斯基脑洞了一道难度为 h1 的题目,然而吉米多出题斯基觉得太水了,于是把这题稍微改了改变成了一道难度为 h2 的。然而吉米多出题斯基还是觉得太水了,又稍微改了改变成了难度为 h3 的。如此进行下去吉米多出题斯基脑洞出了 n 个版本的题目,难度分别为 h1,,hn

由于吉米多出题斯基在脑洞的时候只是随便改了改题目条件,所以每次改题并不一定会变难。但神奇的是,每次产生一个新版本的题目后,吉米多出题斯基手上所有版本的题目难度的中位数不会降低。

很快吉米多出题斯基出好了题,造好了 UOI,选手们纷纷阵亡。多年后吉米多出题斯基再看到自己曾经出的这道题时感叹道:“都是回忆啊……”

可是吉米多出题斯基突然发现自己只记得脑洞过程产生的 n 个版本难度是 a1,,an,却不记得每个 ai 对应的是第几个版本了。

吉米多出题斯基日理万机没有时间再细想了,于是他找到了你 —— 风璃殇做不出题耶维奇,请你帮助吉米多出题斯基把 a1,,an 排列顺序,给出一组满足条件且字典序最大的 h1,,hn 吧!

对于一个数列 v1,,vm,若 m 为奇数则定义中位数为从小到大第 ?m/2? 的数;若 m 为偶数则定义中位数为从小到大第 m/2 和第 m/2+1 的数的平均值。

输入

第一行一个正整数 n

接下来一行 n 个整数 a1,,an

输出

一行,n 个整数表示你找到的字典序最大的 h1,,hn

如果无解,输出卖萌表情 "QwQ"。

输入示例

8
1 2 2 3 3 3 4 4

输出示例

3 3 4 3 4 2 2 1

数据规模及约定

1n105, 1ai109

题解

丢链接跑。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;

int read() {
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = getchar(); }
	while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = getchar(); }
	return x * f;
}

#define maxn 100010
struct Node {
	int v, r, siz;
	Node() {}
	Node(int _, int __): v(_), r(__) {}
} ns[maxn<<1];
int rt1, rt2, ToT, fa[maxn<<1], ch[maxn<<1][2];
void maintain(int o) {
	ns[o].siz = 1;
	for(int i = 0; i < 2; i++) if(ch[o][i])
		ns[o].siz += ns[ch[o][i]].siz;
	return ;
}
void rotate(int u) {
	int y = fa[u], z = fa[y], l = 0, r = 1;
	if(z) ch[z][ch[z][1]==y] = u;
	if(ch[y][1] == u) swap(l, r);
	fa[u] = z; fa[y] = u; fa[ch[u][r]] = y;
	ch[y][l] = ch[u][r]; ch[u][r] = y;
	maintain(y); maintain(u);
	return ;
}
void insert(int& o, int v) {
	if(!o) {
		ns[o = ++ToT] = Node(v, rand());
		return maintain(o);
	}
	bool d = v > ns[o].v;
	insert(ch[o][d], v); fa[ch[o][d]] = o;
	if(ns[ch[o][d]].r > ns[o].r) {
		int t = ch[o][d];
		rotate(t); o = t;
	}
	return maintain(o);
}
void del(int& o, int v) {
	if(!o) return ;
	if(ns[o].v == v) {
		if(!ch[o][0] && !ch[o][1]) o = 0;
		else if(!ch[o][0]) {
			int t = ch[o][1]; fa[t] = fa[o]; o = t;
		}
		else if(!ch[o][1]) {
			int t = ch[o][0]; fa[t] = fa[o]; o = t;
		}
		else {
			bool d = ns[ch[o][1]].r > ns[ch[o][0]].r;
			int t = ch[o][d]; rotate(t); o = t;
			del(ch[o][d^1], v);
		}
	}
	else {
		bool d = v > ns[o].v;
		del(ch[o][d], v);
	}
	return maintain(o);
}
int Find(int o, int v) {
	if(!o) return 0;
	int ls = ch[o][0] ? ns[ch[o][0]].siz : 0;
	if(v >= ns[o].v) return ls + 1 + Find(ch[o][1], v);
	return Find(ch[o][0], v);
}
int qkth(int o, int k) {
	if(!o) return 0;
	int ls = ch[o][0] ? ns[ch[o][0]].siz : 0;
	if(k == ls + 1) return ns[o].v;
	if(k > ls + 1) return qkth(ch[o][1], k - ls - 1);
	return qkth(ch[o][0], k);
}
int Findlow(int o, int v) {
	if(!o) return 0;
	int ls = ch[o][0] ? ns[ch[o][0]].siz : 0;
	if(v >= ns[o].v) return max(ns[o].v, Findlow(ch[o][1], v));
	return Findlow(ch[o][0], v);
}

int A[maxn], ans[maxn], cnt;
int main() {
	int n = read();
	for(int i = 1; i <= n; i++) A[i] = read();
	
	sort(A + 1, A + n + 1);
	for(int i = 1; i <= n; i++) insert(rt2, A[i]);
	int m = (n & 1) ? A[n+1>>1] : (A[n>>1] + A[(n>>1)+1] >> 1), tmp = -1;
	for(int i = n; i > 1; i--) if(A[i] <= m && A[i] == A[i-1]) {
		tmp = i; break;
	}
	m = 0;
	if(tmp >= 0) {
		m = A[tmp];
		insert(rt1, A[tmp]); insert(rt1, A[tmp]); del(rt2, A[tmp]); del(rt2, A[tmp]);
		ans[++cnt] = A[tmp]; ans[++cnt] = A[tmp];
		for(int i = tmp - 2; i; i--) {
			int x = qkth(rt2, ns[rt2].siz);
			if(x > A[tmp]) {
				ans[++cnt] = x;
				del(rt2, x); insert(rt1, x);
			}
			ans[++cnt] = A[i];
			del(rt2, A[i]); insert(rt1, A[i]);
		}
		int x = qkth(rt2, ns[rt2].siz);
		if(x > A[tmp]) {
			ans[++cnt] = x;
			del(rt2, x); insert(rt1, x);
		}
	}
	while(rt2) {
		int k = qkth(rt2, 1);
		if(ns[rt1].siz & 1) {
			if(Find(rt1, m) < Find(rt1, k)) {
				int x = qkth(rt2, ns[rt2].siz);
				ans[++cnt] = x;
				del(rt2, x); insert(rt1, x);
			}
			else {
				if(Find(rt1, m) < Find(rt1, 2 * k - m)) {
					int x = qkth(rt2, ns[rt2].siz);
					ans[++cnt] = x;
					del(rt2, x); insert(rt1, x);
				}
				else {
					int x = Findlow(rt2, 2 * k - m);
					ans[++cnt] = x;
					del(rt2, x); insert(rt1, x);
				}
			}
		}
		else {
			if(Find(rt1, m) < Find(rt1, k)) {
				int x = qkth(rt2, ns[rt2].siz);
				ans[++cnt] = x;
				del(rt2, x); insert(rt1, x);
			}
			else {
				ans[++cnt] = k;
				del(rt2, k); insert(rt1, k);
			}
		}
		m = (ns[rt1].siz & 1) ? qkth(rt1, ns[rt1].siz + 1 >> 1) : (qkth(rt1, ns[rt1].siz >> 1) + qkth(rt1, (ns[rt1].siz >> 1) + 1) >> 1);
	}
	
	for(int i = 1; i <= cnt; i++) printf("%d%c", ans[i], i < cnt ? ‘ ‘ : ‘\n‘);
	
	return 0;
}

 

【UTR #2】[UOJ#278]题目排列顺序 [UOJ#279]题目交流通道 [UOJ#280]题目难度提升