8月27日模拟赛题解

前言

IT WAS NEVER MEANT TO BE.

​ ———Eret & Wilbursoot

暑假的最后一场模拟赛,无论是好是坏,都是一个标志性的结束。

但是结果很差。

\(\text{T1}\):签到题(有人写挂了),评分 \(10\),有 \(114514\times1919810\) 种方法能 \(\text{AC}\)

\(\text{T2}\):逆康托展开 \(+\) 压位高精 \(+\) 数论 \(+\) 二分查找 \(+\) 树状数组!!!评分unsigned __int256 score=-1;

\(\text{T3}\):数论,评分 \(30\)

\(\text{T4}\):二分答案 \(+\) 并查集 \(/\) 最短路,很套路,评分 \(30\)

然后 \(10min\) 写完 \(\text{T1}\),乱推 \(\text{T2,T3}\),写了个 \(\rm BFS\) 乱搞 \(\text{T4}\) 发现搞不了无向边,最后 \(\text{T2}\,\rm DFS\)\(30\)\(\text{T3}\) 搞了个质数筛 \(+\) \(\mathcal{O}(n)\) 判断骗 \(60\) 分,\(\text{T4}\) 交了 \(\rm BFS\) 竟然骗了 \(10\) 分。

但是 \(\text{T4}\) 和几乎刚考过的比赛中的 \(\text{T3}\) 很像啊!8月10日模拟赛题解-T3

正文

\(\text{T1}\) 序列

\(\text{Description}\)

有一个长度为 \(n(n\le2\times10^5)\) 的序列 \(a\) 和一个初始为空的序列 \(b\),将依次进行 \(n\) 次操作,其中第 \(i\) 次操作分为以下两步:

  1. \(a_i\) 加到序列 \(b\) 的尾部;
  2. 翻转序列 \(b\)

请求出 \(n\) 次操作之后的序列 \(b\)

\(\text{Solution}\)

直接模拟即可,这里使用双端队列,每操作一次就换一个方向插入,最后判断从哪个方向输出。

时间复杂度 \(\mathcal{O}(n)\)

\(\text{Code}\)

#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;

int a[200005];
deque<int> dq;

int main()
{
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
	{
		scanf("%d", a + i);
		if (i & 1)
		{
			dq.push_front(a[i]);
		}
		else
		{
			dq.push_back(a[i]);
		}
	}
	if (n & 1)
	{
		while (!dq.empty())
		{
			printf("%d ", dq.front());
			dq.pop_front();
		}
	}
	else
	{
		while (!dq.empty())
		{
			printf("%d ", dq.back());
			dq.pop_back();
		}
	}
	return 0;
}

\(\text{T3}\) 简单数学题

\(\text{Description}\)

对于一个正整数 \(N(N\le10^{14})\),求出所有的正整数 \(T\),使得 \(\dfrac{N-\dfrac{1}{2}T}{N-T}\in\mathbb{Z}^+\)

\(\text{Solution}\)

乱推式子,只要能推出来。

\(\dfrac{N-\dfrac{1}{2}T}{N-T}=K\),则 \(K\in\mathbb{Z}^+\)

\[\dfrac{N-\dfrac{1}{2}T}{N-T}=K \]

\[\dfrac{2N-T}{2N-2T}=K \]

\[2N-T=2KN-2KT \]

\[2KN-2N=2KT-T \]

\[(2K-2)N=(2K-1)T \]

\[\dfrac{2K-2}{2K-1}N=T \]

\(\because \gcd(2K-2,2K-1)=1\)

\(\therefore (2K-1)\)\(N\) 的因数,且 \((2K-1)\) 是奇数。

\(\therefore (2K-1)\)\(N\) 的奇因数。

那么筛出 \(N\) 的所有奇因数(\(1\) 除外),这个是 \(\mathcal{O}(\sqrt{N})\) 的。

因为筛出来的是打乱顺序的,所以要 \(\operatorname{sort}\) 一遍,这个是 \(\mathcal{O}(N\log N)\) 的。

最后 \(\operatorname{for}\) 一遍输出 \(\dfrac{X-1}{X}N\)\(X\)\(N\) 的奇因数) 即可。

时间复杂度 \(\mathcal{O}(N\log N)\)

\(\text{Code}\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#define int long long
using namespace std;

const int MAXN = 2e7 + 5;

int cnt;
int v[MAXN];

signed main()
{
	int n;
	scanf("%lld", &n);
	if (n == 1)
	{
		puts("0");
		return 0;
	}
	for (int i = 2; i * i <= n; i++)
	{
		if (n % i == 0)
		{
			if (i & 1)
			{
				v[++cnt] = i;
			}
			if ((n / i) & 1)
			{
				v[++cnt] = n / i;
			}
		}
	}
	if (n & 1)
	{
		v[++cnt] = n;
	}
	printf("%lld ", cnt);
	sort(v + 1, v + cnt + 1);
	for (int i = 1; i <= cnt; i++)
	{
		printf("%lld ", n / v[i] * (v[i] - 1));
	}
	return 0;
}

\(\text{T4}\) 遨游

\(\text{Descrition}\)

给定一张有 \(n\) 个点 \(m\) 条边无向连通图,第 \(i\) 条边连接着 \(u_i\)\(v_i\) 有边权 \(w_i(w_i\le15000)\),要从节点 \(s\) 走到节点 \(t\),请求出一对 \(L,R\in\mathbb{Z}^+\),满足

  1. \(L\le R\)
  2. 只在图中保留边权在 \([L,R]\) 范围中的边后,能从 \(s\) 走到 \(t\)
  3. \(L\) 要尽可能大,\(R\) 在满足 \(L\) 尽量大的基础上尽量小。

\(\text{Solution1}\)

显然 \(L=\left\lfloor\min\{w_i\}\right\rfloor,R=\left\lceil\max\{w_i\}\right\rceil\)

看到最小值最大最大值最小立马想到二分答案。

先二分 \(L\),用并查集维护,将 \(w_i\ge mid\) 的所有边 \(i\)\(u_i\)\(v_i\)并入同一个集合,最后判断 \(s\)\(t\) 是否在同一个集合即可。

得到 \(L\) 后存下来,再二分 \(R\),注意要在满足 \(w_i\ge L\) 的基础上,所以将 \(L\le w_i\le mid\) 的所有边 \(i\)\(u_i\)\(v_i\) 并入同一个集合,然后判断。

时间复杂度为 \(\mathcal{O}(\log 15000\times m\times\alpha(m))\approx \mathcal{O}(50m)\)

\(\text{Code1}\)

#include <iostream>
#include <cstdio>
#include <queue>
#include <cmath>
using namespace std;

const int MAXN = 50005;
const int MAXM = 1e5 + 5;
const int INF = 0x3f3f3f3f;

int n, m, s, t, tot;
int c[MAXN], x[MAXN], fa[MAXN];

struct edge
{
	int u, v;
	double w;
}e[MAXM << 1];

void init()
{
	for (int i = 1; i <= tot; i++)
	{
		fa[i] = i;
	}
}

int find(int x)
{
	if (x == fa[x])
	{
		return x;
	}
	return fa[x] = find(fa[x]);
}

void merge(int x, int y)
{
	x = find(x), y = find(y);
	if (x != y)
	{
		fa[y] = x;
	}
}

bool check(int l, int r)
{
	init();
	for (int i = 1; i <= m; i++)
	{
		if (l <= e[i].w && e[i].w <= r)
		{
			merge(e[i].u, e[i].v);
		}
	}
	return find(s) == find(t);
}

int main()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; i++)
	{
		scanf("%d%d%lf", &e[i].u, &e[i].v, &e[i].w);
	}
	for (int i = 1; i <= n; i++)
	{
		int t;
		scanf("%d", &t);
		tot += t;
		for (int j = 1; j <= t; j++)
		{
			int u;
			scanf("%d", &u);
			c[u] = i;
		}
	}
	for (int i = 1; i <= n; i++)
	{
		scanf("%d", x + i);
	}
	for (int i = 1; i <= m; i++)
	{
		if (c[e[i].u] == c[e[i].v]) // 题目要求,乱搞
		{
			e[i].w = e[i].w * x[c[e[i].u]] / 100;
		}
		else
		{
			e[i].w = e[i].w * (x[c[e[i].u]] + x[c[e[i].v]]) / 200;
		}
	}
	scanf("%d%d", &s, &t);
	int l = 0, r = 15000;
	while (l < r)
	{
		int mid = (l + r + 1) >> 1;
		if (check(mid, INF))
		{
			l = mid;
		}
		else
		{
			 r = mid - 1;
		}
	}
	int ansl = l;
	printf("%d ", l);
	l = 0, r = 15000;
	while (l < r)
	{
		int mid = (l + r) >> 1;
		if (check(ansl, mid))
		{
			r = mid;
		}
		else
		{
			l = mid + 1;
		}
	}
	printf("%d\n", l);
	return 0;
}

\(\text{Solution2}\)

跑两遍 \(\rm Dijkstra\) 即可。

时间复杂度 \(\mathcal{O}((m+15000)\log15000)\approx\mathcal{O}(15m)\)

\(\text{Code2}\)

#include <iostream>
#include <cstdio>
#include <queue>
#include <cmath>
using namespace std;

const int MAXN = 50005;
const int MAXM = 1e5 + 5;

int n, m, s, t, cnt, tot, ansl;
int head[MAXN], u[MAXM], v[MAXM], c[MAXN], x[MAXN];
double w[MAXM], dis[MAXN];
bool vis[MAXN];

struct edge
{
	int to;
	double dis;
	int nxt;
}e[MAXM << 1];

void add(int u, int v, double w)
{
	e[++cnt] = edge{v, w, head[u]};
	head[u] = cnt;
}

struct node_L
{
	int from;
	double minn;
	bool operator <(const node_L &x)const
	{
		return x.minn > minn;
	}
};

void dijkstra_L()
{
	priority_queue<node_L> pq;
	pq.push(node_L{s, 0x3f3f3f3f});
	while (!pq.empty())
	{
		int u = pq.top().from;
		double mi = pq.top().minn;
		pq.pop();
		if (vis[u])
		{
			continue;
		}
		vis[u] = true;
		for (int i = head[u]; i; i = e[i].nxt)
		{
			int v = e[i].to;
			double nowm = min(mi, e[i].dis);
			if (dis[v] < nowm)
			{
				dis[v] = nowm;
				pq.push(node_L{v, dis[v]});
			}
		}
	}
	ansl = dis[t];
}

struct node_R
{
	int from;
	double maxx;
	bool operator <(const node_R &x)const
	{
		return x.maxx < maxx;
	}
};

void dijkstra_R()
{
	for (int i = 1; i <= tot; i++)
	{
		dis[i] = 0x3f3f3f3f;
		vis[i] = false;
	}
	priority_queue<node_R> pq;
	pq.push(node_R{s, 0});
	dis[s] = 0;
	while (!pq.empty())
	{
		int u = pq.top().from;
		double ma = pq.top().maxx;
		pq.pop();
		if (vis[u])
		{
			continue;
		}
		vis[u] = true;
		for (int i = head[u]; i; i = e[i].nxt)
		{
			int v = e[i].to;
			double nowm = max(ma, e[i].dis);
			if (dis[v] > nowm && e[i].dis >= ansl)
			{
				dis[v] = nowm;
				pq.push(node_R{v, dis[v]});
			}
		}
	}
}

int main()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; i++)
	{
		scanf("%d%d%lf", u + i, v + i, w + i);
	}
	for (int i = 1; i <= n; i++)
	{
		int t;
		scanf("%d", &t);
		tot += t;
		for (int j = 1; j <= t; j++)
		{
			int u;
			scanf("%d", &u);
			c[u] = i;
		}
	}
	for (int i = 1; i <= n; i++)
	{
		scanf("%d", x + i);
	}
	for (int i = 1; i <= m; i++)
	{
		if (c[u[i]] == c[v[i]])
		{
			w[i] = w[i] * x[c[u[i]]] / 100;
		}
		else
		{
			w[i] = w[i] * (x[c[u[i]]] + x[c[v[i]]]) / 200;
		}
		add(u[i], v[i], w[i]);
		add(v[i], u[i], w[i]);
	}
	scanf("%d%d", &s, &t);
	dijkstra_L();
	printf("%d ", (int)(floor(dis[t])));
	dijkstra_R();
	printf("%d\n", (int)(ceil(dis[t])));
	return 0;
}

\(*\text{T2}\) 今天你 \(\text{AK}\) 了吗?

\(\text{Description}\)

给定 \(n(1\le n\le100000)\)\(k(1\le k\le\min(10^{20000},n!))\),求出字典序第 \(k\) 小的 \(n\) 的排列。

\(\text{Solution}\)

Q:为什么放在了最后还加了个 \(*\) 号?

A:因为这题太毒瘤了。

然后作者太懒了,所以懒得写了。

\(\text{Code}\)

#include <iostream>
#include <cstdio>
#include <cstring>
#define int long long
using namespace std;

const int MAXN = 1e5 + 5;

int n, len;
int a[MAXN], mod[MAXN], c[MAXN];
char k[MAXN];

int div(int x)
{	
	int r = 0;
	for (int i = len; i >= 0; i--)
	{
		int s = r * (int)(1e13) + a[i];
		a[i] = s / x;
		r = s % x;
	}
	while (!a[len])
	{
		len--;
	}
	return r;
}

int lowbit(int x)
{
	return x & -x;
}

void update(int x)
{
	for (int i = x; i <= n; i += lowbit(i))
	{
		c[i]++;
	}
}

int query(int x)
{
	int res = 0;
	for (int i = x; i; i -= lowbit(i))
	{
		res += c[i];
	}
	return res;
}

signed main()
{
	scanf("%lld%s", &n, k);
	len = strlen(k);
	int pos = 0;
	for (int i = len - 1; i >= 0; i--) 
	{
		if ((len - i - 1) % 13 == 0)
		{
			pos++;
		}
		mod[i] = pos - 1;
	}
	for (int i = 0; i < len; i++)
	{
		a[mod[i]] = a[mod[i]] * 10 + k[i] - 48;
	}
	a[0]--;
	for (int i = 0; i < len; i++)
	{
		if (a[i] < 0)
		{
			a[i + 1]--;
			a[i] += (int)(1e13);
		}
	}
	len = pos - 1;
	for (int i = 1; i <= n; i++)
	{
		mod[n - i + 1] = div(i);
	}
	for (int i = 1; i <= n; i++)
	{
		int l = 1, r = n;
		while (l < r)
		{
			int mid = (l + r) >> 1;
			int x = mid - query(mid);
			if (x <= mod[i])
			{
				l = mid + 1;
			}
			else
			{
				r = mid;
			}
		}
		update(l);
		printf("%lld ", l);
	}
	return 0;
}
posted @ 2021-08-27 21:28  mango09  阅读(57)  评论(0编辑  收藏  举报
-->