2021洛谷10月月赛3游记

传送门

【 L G R − 095 】 洛 谷   10   月 月 赛   I I I   & 「 L Z O I 」 R o u n d   ⋅   01 \rm【LGR-095】洛谷~10~月月赛~III~\&「LZOI」Round~·~01 LGR095 10  III &LZOIRound  01

A 潇湘の雨

Sol

考虑一次遍历是 n 2 n^2 n2 的,所以若遍历 k k k 次,那么是

⌊ k n 2 ⌋ \left\lfloor\dfrac{k}{n^2}\right\rfloor n2k

轮再加上一些点。

不难发现还剩

  k m o d    n 2 =   k − k × ⌊ k n 2 ⌋ \begin{aligned}&~k\mod n^2\\=&~k-k\times\left\lfloor\dfrac{k}{n^2}\right\rfloor\end{aligned} = kmodn2 kk×n2k

个点。

那么既然我们可以找每个 ( x , y ) (x,y) (x,y) 的下一个点, < n 2 <n^2 <n2 个点是不需要考虑的。

据此答案即为

⌊ k n 2 ⌋ \left\lfloor\dfrac{k}{n^2}\right\rfloor n2k

std
signed main(){
	int n = init(), k = init();
	print(k / (n * n * 4));
}

B 儒略の日

Sol

首先考虑所有奇数,它们两次出现位置要挨得越远越好。

那么我们就会考虑这样摆:i ... ... ... ... ... i,其中 i i i 是那个奇数。

然后中间尽可能地摆一大堆数。

另一方面,是所有偶数,它们挨得越近越好。那干脆就某个偶数 j j j,一旦出现,就是 ... ... ... j j ... ... ... 这种形式,近得不能再近。

然后考虑一个平均的思想:所有奇数都要很远很远,那么显然有一种策略:

奇数 奇数 奇数 偶数 偶数 偶数 偶数 ... ... 偶数 偶数 偶数 偶数 奇数 奇数 奇数

简而言之就是用所有的偶数把这些奇数隔得越远越好。

另一方面,形如 1 3 偶 偶 偶 偶 ... 偶 偶 偶 偶 3 1 这种策略,一定不如 1 3 偶 偶 偶 偶 ... 偶 偶 偶 偶 1 3 这种。

所以最终得到摆放的方案(经过上述考虑,这种就是最优的):

1 3 5 7 9 ... 2k+1 | 2 2 4 4 6 6 8 8 ... 2k 2k | 1 3 5 7 9 ... 2k+1

我特意用 | 隔开了奇数和偶数所占领的区域。

std
#include<cstdio>
int init(){
	char c = getchar();
	int x = 0, f = 1;
	for (; c < '0' || c > '9'; c = getchar())
		if (c == '-') f = -1;
	for (; c >= '0' && c <= '9'; c = getchar())
		x = (x << 1) + (x << 3) + (c ^ 48);
	return x * f;
}
void print(int x){
	if (x < 0) x = -x, putchar('-');
	if (x > 9) print(x / 10);
	putchar(x % 10 + '0');
}
const int MAXN = 2000005;
int a[MAXN];
int main(){
	int n = init(), d = init(), nxt = 1;
	for (int i = 1; i <= n; i += 2)
		a[nxt++] = i;
	for (int i = 2; i <= n; i += 2)
		a[nxt++] = a[nxt++] = i;
	if (nxt - 1 <= d){
		print(-1);
		return 0;
	}
	for (int i = 1; i <= n; i += 2)
		a[nxt++] = i;
	for (int i = 1; i < nxt; ++i)
		print(a[i]), putchar(' ');
}

C 兜心の顶

下文中「唯一性」代指直径、重心、直径重心三个「唯一」,「不等性」代指树的重心不等于直径重心。

一、直径的长度讨论

首先直径 不会是偶数

否则设直径的长度是 2 k 2k 2k,由 1 ⋯ 2 k 1\cdots 2k 12k 2 k 2k 2k 个结点组成。

那么结点 k k k 和结点 k + 1 k+1 k+1 必然都是直径重心,与唯一性不符,舍去。

然后直径不可能是 1 1 1:显然。

直径不可能是 3 , 5 3,5 3,5:此时重心只能出现在中间的点(否则与唯一性不符),与不等性不符。

综上,直径长度至少为 7 7 7 且必为奇数。

二、重心位置的讨论

不妨钦定我们构造的树的直径就是 7 7 7,下面来看看位置:

首先直径的重心一定是 1 ⋯ 7 1\cdots7 17 号结点的中点: 4 4 4 号结点。

那么树的重心一定要出现在其他结点上,首先 1 , 2 , 6 , 7 1,2,6,7 1,2,6,7 被排除,理由是唯一性。

那么只剩下 3 , 5 3,5 3,5 两个对称的结点,我们不妨设重心是 3 3 3 号结点。

此时需要给 3 3 3 号结点再安排上一些子树,为了不影响唯一性, 3 3 3 的其他子树深度只能为 1 1 1

那么考虑到 3 3 3 必须是唯一重心,至少要加入两个新结点,才能保证。

综上,树的大小至少为 9 9 9,且形态为:

1 ↔ 2 ↔ 3 ↔ 4 ↔ 5 ↔ 6 ↔ 7 1\leftrightarrow2\leftrightarrow\color{red}3\color{black}\leftrightarrow\color{blue}4\color{black}\leftrightarrow5\leftrightarrow6\leftrightarrow7 1234567

8 ↔ 3 ↔ 9 8\leftrightarrow\color{red}3\color{black}\leftrightarrow9 839

其中 3 \color{red}3 3 是树的重心, 4 \color{blue}4 4 是直径重心,上面的这一条链就是直径。

三、N >= 10 情况的讨论

不难发现,只要我们继续按照该策略,不断地给结点 3 3 3 添加新的子结点(形成一张菊花图)即可。

四、代码实现

N ≤ 8 N\le8 N8 时:输出 -1

其他情况,先输出

1 ↔ 2 ↔ 3 ↔ 4 ↔ 5 ↔ 6 ↔ 7 1\leftrightarrow2\leftrightarrow\color{red}3\color{black}\leftrightarrow\color{blue}4\color{black}\leftrightarrow5\leftrightarrow6\leftrightarrow7 1234567

这个基本模型,然后对于 i = 8 ∼ N i=8\sim N i=8N 的所有结点 i i i,全部连向结点 3 3 3 即可。

std
#include<cstdio>
int init(){
	char c = getchar();
	int x = 0, f = 1;
	for (; c < '0' || c > '9'; c = getchar())
		if (c == '-') f = -1;
	for (; c >= '0' && c <= '9'; c = getchar())
		x = (x << 1) + (x << 3) + (c ^ 48);
	return x * f;
}
int print(int x){
	if (x < 0) x = -x, putchar('-');
	if (x > 9) print(x / 10);
	putchar(x % 10 + '0');
	return 0;
}
int main(){
	int n = init();
	if (n <= 8) return print(-1);
	print(n), putchar('\n');
	for (int i = 1; i <= 6; ++i)
		print(i), putchar(' '), print(i+1), putchar('\n');
	for (int i = 8; i <= n; ++i)
		print(3), putchar(' '), print(i), putchar('\n');
}
五、后记

本题显然有很多合理的构造,但是我发现本构造最为清晰、整洁、易懂。

D 火烧の云

Sol

考虑最短路,设一个三元组 d i s ( i , j , k ) dis(i,j,k) dis(i,j,k) 表示到达位置 ( i , j ) (i,j) (i,j) 且方向状态为 k k k 时所需最少步数。

初始化:所有点位置字符导致的连边、权值更新; d i s ( S i , S j , { 0 , 1 , 2 , 3 } ) = 0 dis(S_i,S_j,\{0,1,2,3\})=0 dis(Si,Sj,{0,1,2,3})=0,最终答案: min ⁡ { d i s ( E i , E j , { 0 , 1 , 2 , 3 } ) } \min\{dis(E_i,E_j,\{0,1,2,3\})\} min{dis(Ei,Ej,{0,1,2,3})}

注意多个起点多个终点的情况需要灵活地处理,考虑将所有起点和终点吊起来导向同一个虚拟根节点即可。

实现

设一个三元组

( i , j , 0 ) 表示到达点 ( i , j ) , 来向 是 北 方向 ( i , j , 1 ) 表示到达点 ( i , j ) , 来向 是 东 方向 ( i , j , 2 ) 表示到达点 ( i , j ) , 来向 是 南 方向 ( i , j , 3 ) 表示到达点 ( i , j ) , 来向 是 西 方向 \begin{aligned}(i,j,0)\text{表示到达点}(i,j)\text{,\color{red}{来向}}\text{是}\text{ 北 }\text{方向}\\(i,j,1)\text{表示到达点}(i,j)\text{,\color{red}{来向}}\text{是}\text{ 东 }\text{方向}\\(i,j,2)\text{表示到达点}(i,j)\text{,\color{red}{来向}}\text{是}\text{ 南 }\text{方向}\\(i,j,3)\text{表示到达点}(i,j)\text{,\color{red}{来向}}\text{是}\text{ 西 }\text{方向}\end{aligned} (i,j,0)表示到达点(i,j)  方向(i,j,1)表示到达点(i,j)  方向(i,j,2)表示到达点(i,j)  方向(i,j,3)表示到达点(i,j) 西 方向

那么对于下面的这些字符:

字符类型 ( i , j , 0 ) → (i,j,0)\rightarrow (i,j,0) ( i , j , 1 ) → (i,j,1)\rightarrow (i,j,1) ( i , j , 2 ) → (i,j,2)\rightarrow (i,j,2) ( i , j , 3 ) → (i,j,3)\rightarrow (i,j,3)
\| a a a ( i + 1 , j , 0 ) (i+1,j,0) (i+1,j,0) ( i − 1 , j , 2 ) , ( i + 1 , j , 0 ) (i-1,j,2),(i+1,j,0) (i1,j,2),(i+1,j,0) ( i − 1 , j , 2 ) (i-1,j,2) (i1,j,2) ( i − 1 , j , 2 ) , ( i + 1 , j , 0 ) (i-1,j,2),(i+1,j,0) (i1,j,2),(i+1,j,0)
- a a a ( i , j − 1 , 1 ) , ( i , j + 1 , 3 ) (i,j-1,1),(i,j+1,3) (i,j1,1),(i,j+1,3) ( i , j − 1 , 1 ) (i,j-1,1) (i,j1,1) ( i , j − 1 , 1 ) , ( i , j + 1 , 3 ) (i,j-1,1),(i,j+1,3) (i,j1,1),(i,j+1,3) ( i , j + 1 , 3 ) (i,j+1,3) (i,j+1,3)
/ b b b ( i , j − 1 , 1 ) (i,j-1,1) (i,j1,1) ( i + 1 , j , 0 ) (i+1,j,0) (i+1,j,0) ( i , j + 1 , 3 ) (i,j+1,3) (i,j+1,3) ( i − 1 , j , 2 ) (i-1,j,2) (i1,j,2)
\ b b b ( i , j + 1 , 3 ) (i,j+1,3) (i,j+1,3) ( i − 1 , j , 2 ) (i-1,j,2) (i1,j,2) ( i , j − 1 , 1 ) (i,j-1,1) (i,j1,1) ( i + 1 , j , 0 ) (i+1,j,0) (i+1,j,0)
. c c c ( i + 1 , j , 0 ) , ( i , j − 1 , 1 ) , ( i − 1 , j , 2 ) , ( i , j + 1 , 3 ) (i+1,j,0),(i,j-1,1),(i-1,j,2),(i,j+1,3) (i+1,j,0),(i,j1,1),(i1,j,2),(i,j+1,3) ( i + 1 , j , 0 ) , ( i , j − 1 , 1 ) , ( i − 1 , j , 2 ) , ( i , j + 1 , 3 ) (i+1,j,0),(i,j-1,1),(i-1,j,2),(i,j+1,3) (i+1,j,0),(i,j1,1),(i1,j,2),(i,j+1,3) ( i + 1 , j , 0 ) , ( i , j − 1 , 1 ) , ( i − 1 , j , 2 ) , ( i , j + 1 , 3 ) (i+1,j,0),(i,j-1,1),(i-1,j,2),(i,j+1,3) (i+1,j,0),(i,j1,1),(i1,j,2),(i,j+1,3) ( i + 1 , j , 0 ) , ( i , j − 1 , 1 ) , ( i − 1 , j , 2 ) , ( i , j + 1 , 3 ) (i+1,j,0),(i,j-1,1),(i-1,j,2),(i,j+1,3) (i+1,j,0),(i,j1,1),(i1,j,2),(i,j+1,3)
< d d d ( i , j − 1 , 1 ) (i,j-1,1) (i,j1,1) ( i , j − 2 , 1 ) \color{red}(i,j-2,1) (i,j2,1) ( i , j − 1 , 1 ) (i,j-1,1) (i,j1,1) × \times ×
> d d d ( i , j + 1 , 3 ) (i,j+1,3) (i,j+1,3) × \times × ( i , j + 1 , 3 ) (i,j+1,3) (i,j+1,3) ( i , j + 2 , 3 ) \color{red}(i,j+2,3) (i,j+2,3)
^ d d d × \times × ( i − 1 , j , 2 ) (i-1,j,2) (i1,j,2) ( i − 2 , j , 2 ) \color{red}(i-2,j,2) (i2,j,2) ( i − 1 , j , 2 ) (i-1,j,2) (i1,j,2)
v d d d ( i + 2 , j , 0 ) \color{red}(i+2,j,0) (i+2,j,0) ( i + 1 , j , 0 ) (i+1,j,0) (i+1,j,0) × \times × ( i + 1 , j , 0 ) (i+1,j,0) (i+1,j,0)
S 0 0 0 ( i + 1 , j , 0 ) , ( i , j − 1 , 1 ) , ( i − 1 , j , 2 ) , ( i , j + 1 , 3 ) (i+1,j,0),(i,j-1,1),(i-1,j,2),(i,j+1,3) (i+1,j,0),(i,j1,1),(i1,j,2),(i,j+1,3) ( i + 1 , j , 0 ) , ( i , j − 1 , 1 ) , ( i − 1 , j , 2 ) , ( i , j + 1 , 3 ) (i+1,j,0),(i,j-1,1),(i-1,j,2),(i,j+1,3) (i+1,j,0),(i,j1,1),(i1,j,2),(i,j+1,3) ( i + 1 , j , 0 ) , ( i , j − 1 , 1 ) , ( i − 1 , j , 2 ) , ( i , j + 1 , 3 ) (i+1,j,0),(i,j-1,1),(i-1,j,2),(i,j+1,3) (i+1,j,0),(i,j1,1),(i1,j,2),(i,j+1,3) ( i + 1 , j , 0 ) , ( i , j − 1 , 1 ) , ( i − 1 , j , 2 ) , ( i , j + 1 , 3 ) (i+1,j,0),(i,j-1,1),(i-1,j,2),(i,j+1,3) (i+1,j,0),(i,j1,1),(i1,j,2),(i,j+1,3)
E 0 0 0 End \texttt{End} End End \texttt{End} End End \texttt{End} End End \texttt{End} End

除表格内的边,另外要连上

Start → → → → j u [ i ] [ j ] = = "S" ( i , j , 0 ) Start → → → → j u [ i ] [ j ] = = "S" ( i , j , 1 ) Start → → → → j u [ i ] [ j ] = = "S" ( i , j , 2 ) Start → → → → j u [ i ] [ j ] = = "S" ( i , j , 3 ) \begin{aligned}\texttt{Start}\underset{ju[i][j]==\texttt{"S"}}{\rightarrow\rightarrow\rightarrow\rightarrow}(i,j,0)\\\texttt{Start}\underset{ju[i][j]==\texttt{"S"}}{\rightarrow\rightarrow\rightarrow\rightarrow}(i,j,1)\\\texttt{Start}\underset{ju[i][j]==\texttt{"S"}}{\rightarrow\rightarrow\rightarrow\rightarrow}(i,j,2)\\\texttt{Start}\underset{ju[i][j]==\texttt{"S"}}{\rightarrow\rightarrow\rightarrow\rightarrow}(i,j,3)\end{aligned} Startju[i][j]=="S"→→→→(i,j,0)Startju[i][j]=="S"→→→→(i,j,1)Startju[i][j]=="S"→→→→(i,j,2)Startju[i][j]=="S"→→→→(i,j,3)

这些边就可以。Start 是超源,End 是超汇,跑一遍单源最短路 dis[Start]=0,最后 dis[End] 就是结果。

std
#include<queue>
#include<cstdio>
#include<cstring>
#define A mp(i, j, 0)
#define B mp(i, j, 1)
#define C mp(i, j, 2)
#define D mp(i, j, 3)
#define Start mp(n, m, 4)
#define End mp(n, m, 5)
int init(){
	char c = getchar();
	int x = 0, f = 1;
	for (; c < '0' || c > '9'; c = getchar())
		if (c == '-') f = -1;
	for (; c >= '0' && c <= '9'; c = getchar())
		x = (x << 1) + (x << 3) + (c ^ 48);
	return x * f;
}
void print(int x){
	if (x < 0) x = -x, putchar('-');
	if (x > 9) print(x / 10);
	putchar(x % 10 + '0');
}
const int N = 2005, M = 4000005, inf = 0x3f3f3f3f;
char ju[N][N]; bool vis[M << 2];
int n, m, a, b, c, d, head[M << 2], sLen, dis[M << 2];
int mp(int x, int y, int z){
	return (x < 1 || x > n || y < 1 || y > m) ? -1 : ((m * (x - 1) + y) << 2) + z;
}
struct Node{
	int next, to, value;
}s[M << 4];
inline void add(int u, int v, int w){
	if (u == -1 || v == -1) return;
	s[++sLen] = (Node){head[u], v, w};
	head[u] = sLen;
}
std::priority_queue<std::pair<int, int> >Q;
inline void dij(){
	memset(dis,0x3f,sizeof(dis));
	Q.push(std::make_pair(inf, Start)), dis[Start] = 0;
	while (!Q.empty()) {
		std::pair<int, int> tp = Q.top(); Q.pop();
		int u = tp.second;
		if (vis[u]) continue;
		vis[u] = 1;
		for (int i = head[u]; i; i = s[i].next) {
			int v = s[i].to, w = s[i].value;
			if (dis[u] + w < dis[v]) {
				dis[v] = dis[u] + w;
				if (!vis[v]) Q.push(std::make_pair(inf - dis[v], v));
			}
		}
	}
}
int main(){
	n = init(), m = init(), a = init(), b = init(), c = init(), d = init();
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= m; ++j) {
			while (ju[i][j] == '\0' || ju[i][j] == ' ' || ju[i][j] == '\n' || ju[i][j] == '\r') ju[i][j] = getchar();
			switch (ju[i][j]) {
				case '|': {
					add(A, mp(i + 1, j, 0), a), add(B, mp(i - 1, j, 2), a), add(B, mp(i + 1, j, 0), a), add(C, mp(i - 1, j, 2), a), add(D, mp(i - 1, j, 2), a), add(D, mp(i + 1, j, 0), a); break;
				}
				case '-': {
					add(A, mp(i, j - 1, 1), a), add(A, mp(i, j + 1, 3), a), add(B, mp(i, j - 1, 1), a), add(C, mp(i, j - 1, 1), a), add(C, mp(i, j + 1, 3), a), add(D, mp(i, j + 1, 3), a); break;
				}
				case '/': {
					add(A, mp(i, j - 1, 1), b), add(B, mp(i + 1, j, 0), b), add(C, mp(i, j + 1, 3), b), add(D, mp(i - 1, j, 2), b); break;
				}
				case '\\': {
					add(A, mp(i, j + 1, 3), b), add(B, mp(i - 1, j, 2), b), add(C, mp(i, j - 1, 1), b), add(D, mp(i + 1, j, 0), b); break;
				}
				case '.': {
					add(A, mp(i + 1, j, 0), c), add(A, mp(i, j - 1, 1), c), add(A, mp(i - 1, j, 2), c), add(A, mp(i, j + 1, 3), c), add(B, mp(i + 1, j, 0), c), add(B, mp(i, j - 1, 1), c), add(B, mp(i - 1, j, 2), c), add(B, mp(i, j + 1, 3), c), add(C, mp(i + 1, j, 0), c), add(C, mp(i, j - 1, 1), c), add(C, mp(i - 1, j, 2), c), add(C, mp(i, j + 1, 3), c), add(D, mp(i + 1, j, 0), c), add(D, mp(i, j - 1, 1), c), add(D, mp(i - 1, j, 2), c), add(D, mp(i, j + 1, 3), c); break;
				}
				case '<': {
					add(A, mp(i, j - 1, 1), d), add(B, mp(i, j - 2, 1), 0), add(C, mp(i, j - 1, 1), d); break;
				}
				case '>': {
					add(A, mp(i, j + 1, 3), d), add(C, mp(i, j + 1, 3), d), add(D, mp(i, j + 2, 3), 0); break;
				}
				case '^': {
					add(B, mp(i - 1, j, 2), d), add(C, mp(i - 2, j, 2), 0), add(D, mp(i - 1, j, 2), d); break;
				}
				case 'v': {
					add(A, mp(i + 2, j, 0), 0), add(B, mp(i + 1, j, 0), d), add(D, mp(i + 1, j, 0), d); break;
				}
				case 'S': {
					add(Start, A, 0), add(Start, B, 0), add(Start, C, 0), add(Start, D, 0), add(A, mp(i + 1, j, 0), 0), add(A, mp(i, j - 1, 1), 0), add(A, mp(i - 1, j, 2), 0), add(A, mp(i, j + 1, 3), 0), add(B, mp(i + 1, j, 0), 0), add(B, mp(i, j - 1, 1), 0), add(B, mp(i - 1, j, 2), 0), add(B, mp(i, j + 1, 3), 0), add(C, mp(i + 1, j, 0), 0), add(C, mp(i, j - 1, 1), 0), add(C, mp(i - 1, j, 2), 0), add(C, mp(i, j + 1, 3), 0), add(D, mp(i + 1, j, 0), 0), add(D, mp(i, j - 1, 1), 0), add(D, mp(i - 1, j, 2), 0), add(D, mp(i, j + 1, 3), 0); break;
				}
				case 'E': {
					add(A, End, 0), add(B, End, 0), add(C, End, 0), add(D, End, 0); break;
				}
			}
		}
	dij();
	print(dis[End] == inf ? -1 : dis[End]), putchar('\n');
}

E 黄牛の争

数学模型

考虑优化 Special Judge 中 win 函数部分的暴力,首先记

  • α = ⌈ B a ⌉ \alpha=\left\lceil\dfrac{B}{a}\right\rceil α=aB 表示 A \tt A A 击败 B \tt B B 所需回合数;

  • β = ⌈ A b ⌉ \beta=\left\lceil\dfrac{A}{b}\right\rceil β=bA 表示 B \tt B B 击败 A \tt A A 所需回合数。

那么:

  • 如果 B \tt B B 是先手,仅需 β ≤ α \beta\le\alpha βα 即可击败 A \tt A A

  • 如果 B \tt B B 是后手,则需 β < α \beta<\alpha β<α 才能击败 A \tt A A

据此,可以得到 B \tt B B 击败 A \tt A A 的条件:

⌈ A b ⌉ + [ b < a ] ≤ ⌈ B a ⌉ \left\lceil\frac{A}{b}\right\rceil+[b<a]\le\left\lceil\frac{B}{a}\right\rceil bA+[b<a]aB

那么 O ( 1 ) O(1) O(1) 的判定胜负的代码:

bool win(int a, int b, int c, int d){
	return (b + c - 1) / c + (c < a) <= (a + d - 1) / a;
}

下面开始分析每个子任务的得分方式。

Subtask 1

c , C c,C c,C 枚举上限 ( M − 1 ) 2 (M-1)^2 (M1)2,考虑优化:

  1. C \texttt{C} C 只需要能击败 B \texttt{B} B,因此 c c c 的枚举上限应为 max ⁡ ( 1 + b , B ) \max(1+b,B) max(1+b,B)

  2. C \texttt{C} C 必须输给 A \texttt{A} A,因此 c c c 的枚举上限应为 max ⁡ ( a , A ) − 1 \max(a,A)-1 max(a,A)1

Subtask 2

固定 c c c 之后, C C C 越大 C \tt C C 越强, C C C 越小 C \tt C C 越弱。

所以 C C C 的合法取值一定在一个区间 [ l , r ] [l,r] [l,r] 上,可以考虑二分 C C C

Subtask 3, 5

留给一些正确性可能看起来很对的奇怪做法。(比如某不知名 O ( M 3 log ⁡ M ) O(\sqrt[3]M\log M) O(3M logM) 的做法)

Subtask 4

那么这部分分显然是希望我们给出一个 O ( M ) O(M) O(M) 的线性做法。

考虑如果我们确定了 c c c,继续推一推式子(其实也就是挪一挪系数),得到:

b × ( ⌊ B c ⌋ + [ c < b ] )   ≤   C   ≤   ( ⌊ A c ⌋ − [ a < c ] ) × a + a − 1 b\times\left(\left\lfloor\dfrac{B}{c}\right\rfloor+[c<b]\right)~\le~C~\le~\left(\left\lfloor\dfrac{A}{c}\right\rfloor-[a<c]\right)\times a+a-1 b×(cB+[c<b])  C  (cA[a<c])×a+a1

据此就可以 O ( 1 ) O(1) O(1) 求出 C C C 的值。

Subtask 6

考虑一种更快的枚举 c c c 的方式。

由于 c c c 作为分母,它的取值只影响

⌈ A c ⌉ , ⌈ B c ⌉ \left\lceil\dfrac{A}{c}\right\rceil,\left\lceil\dfrac{B}{c}\right\rceil cA,cB

的取值,我们可以整除分块枚举 c c c 的取值。

一种上取整转下取整的办法:

⌈ p q ⌉ = ⌊ p − 1 q ⌋ + 1 \left\lceil\dfrac{p}{q}\right\rceil=\left\lfloor\dfrac{p-1}{q}\right\rfloor+1 qp=qp1+1

另外,朴素的整除分块只枚举到

min ⁡ { A , B } \min\{A,B\} min{A,B}

但是我们的 c c c 应当一直到

max ⁡ { A , B } \max\{A,B\} max{A,B}

处都可能具有贡献。

所以我们考虑根据题意分成两段枚举,第一段使用朴素的整除分块。

第二段枚举 min ⁡ ( A , B ) ∼ max ⁡ ( A , B ) \min(A,B)\sim\max(A,B) min(A,B)max(A,B) 之间的答案贡献部分即可。

std
//E1
#include<cstdio>
int init(){
	char c = getchar();
	int x = 0, f = 1;
	for (; c < '0' || c > '9'; c = getchar())
		if (c == '-') f = -1;
	for (; c >= '0' && c <= '9'; c = getchar())
		x = (x << 1) + (x << 3) + (c ^ 48);
	return x * f;
}
void print(int x){
	if (x < 0) x = -x, putchar('-');
	if (x > 9) print(x / 10);
	putchar(x % 10 + '0');
}
bool win(int a, int b, int c, int d){
	return (b + c - 1) / c + (c < a) <= (a + d - 1) / a;
}
int mn(int a, int b){
	return a < b ? a : b;
}
int mx(int a, int b){
	return a > b ? a : b;
}
int main(){
	int q = init();
	while (q--) {
		int a = init(), A = init(), b = init(), B = init();
		if (win(b, B, a, A)) {
			puts("-1 -1");
			continue;
		}
		bool flag = 1;
		for(int x = 1; x <= 100; ++x)
			for(int y = 1; y <= 100 && flag; ++y)
				if (x != a && x != b && win(b, B, x, y) && win(x, y, a, A))
					flag = 0, print(x), putchar(' '), print(y), putchar('\n');
		if(flag)
			print(-1), putchar(' '), print(-1), putchar('\n');
	}
}
//E2
#include<cstdio>
#define int long long
int init(){
	char c = getchar();
	int x = 0, f = 1;
	for (; c < '0' || c > '9'; c = getchar())
		if (c == '-') f = -1;
	for (; c >= '0' && c <= '9'; c = getchar())
		x = (x << 1) + (x << 3) + (c ^ 48);
	return x * f;
}
void print(int x){
	if (x < 0) x = -x, putchar('-');
	if (x > 9) print(x / 10);
	putchar(x % 10 + '0');
}
bool win(int a, int b, int c, int d){
	return (b + c - 1) / c + (c < a) <= (a + d - 1) / a;
}
int mn(int a, int b){
	return a < b ? a : b;
}
int mx(int a, int b){
	return a > b ? a : b;
}
signed main(){
	int q = init();
	while (q--) {
		int a = init(), A = init(), b = init(), B = init();
		int MAX = mn(mx(1 + b, B), mx(a, A) - 1);
		int M = mx(mx(a, A), mx(b, B));
		if (!win(a, A, b, B) || (B > A && b > a)) {
			puts("-1 -1");
			continue;
		}
		bool flag=1;
		for (int c = 1; c <= MAX; ++c) {
			if (c == a || c == b)
				continue;
			int l = 1, r = (M - 1) * (M - 1), ans = -1;
			if (c > a) r = A - 1;
			if (c < b) l = B + 1;
			while (l <= r) {
				int mid = l + r >> 1;
				if (!win(c, mid, a, A))
					r = mid - 1;
				else if (!win(b, B, c, mid))
					l = mid + 1;
				else {
					ans = mid;
					break;
				}
			}
			if (ans != -1) {
				print(c), putchar(' '), print(ans), putchar('\n');
				flag = 0;
				break;
			}
		}
		if (flag)
			puts("-1 -1");
	}
}
//E3
#include<cstdio>
#define int long long
int init(){
	char c = getchar();
	int x = 0, f = 1;
	for (; c < '0' || c > '9'; c = getchar())
		if (c == '-') f = -1;
	for (; c >= '0' && c <= '9'; c = getchar())
		x = (x << 1) + (x << 3) + (c ^ 48);
	return x * f;
}
void print(int x){
	if (x < 0) x = -x, putchar('-');
	if (x > 9) print(x / 10);
	putchar(x % 10 + '0');
}
bool win(int a, int b, int c, int d){
	return (b + c - 1) / c + (c < a) <= (a + d - 1) / a;
}
int a, A, b, B;
int ok(int c){
	if (c == a || c == b)
		return 0;
	int l = b * (B / c + (c < b)),
	r = (A / c - (a < c)) * a + a - 1;
	if (l > r)
		return 0;
	return l + 1;
}
int mn(int a, int b){
	return a < b ? a : b;
}
int mx(int a, int b){
	return a > b ? a : b;
}
signed main(){
	int t = init();
	for (int i = 1; i <= t; ++i) {
		a = init(), A = init(), b = init(), B = init();
		if (!win(a, A, b, B) || (B > A && b > a)) {
			puts("-1 -1");
			continue;
		}
		--A, --B;
		int c = 0, C;
		if (!ok(a + 1) && !ok(b + 1)) {
			for (int i = 1; i <= mx(A, B); ++i)
				if (ok(i)) {
					c = i, C = ok(i);
					break;
				}
		}
		else {
			if (ok(a + 1))
				c = a + 1, C = ok(a + 1);
			if (ok(b + 1))
				c = b + 1, C = ok(b + 1);
		}
		if (c)
			print(c), putchar(' '), print(C), putchar('\n');
		else
			puts("-1 -1");
	}
}
//E4
#include<cstdio>
#define int long long
int init(){
	char c = getchar();
	int x = 0, f = 1;
	for (; c < '0' || c > '9'; c = getchar())
		if (c == '-') f = -1;
	for (; c >= '0' && c <= '9'; c = getchar())
		x = (x << 1) + (x << 3) + (c ^ 48);
	return x * f;
}
void print(int x){
	if (x < 0) x = -x, putchar('-');
	if (x > 9) print(x / 10);
	putchar(x % 10 + '0');
}
bool win(int a, int b, int c, int d){
	return (b + c - 1) / c + (c < a) <= (a + d - 1) / a;
}
int a, A, b, B;
int ok(int c){
	if (c == a || c == b)
		return 0;
	int l = b * (B / c + (c < b)),
	r = (A / c - (a < c)) * a + a - 1;
	if (l > r)
		return 0;
	return l + 1;
}
int mn(int a, int b){
	return a < b ? a : b;
}
int mx(int a, int b){
	return a > b ? a : b;
}
signed main(){
	int t = init();
	for (int i = 1; i <= t; ++i) {
		a = init(), A = init(), b = init(), B = init();
		if (!win(a, A, b, B) || (B > A && b > a)) {
			puts("-1 -1");
			continue;
		}
		--A, --B;
		int c = 0, C;
		if (!ok(a + 1) && !ok(b + 1)) {
			for (int i = 1; i <= A && i <= B; i = mn(A / (A / i), B / (B / i)) + 1)
				if (ok(i)) {
					c = i, C = ok(i);
					break;
				}
			for (int i = mn(A, B) + 1; i <= mx(A, B); i = mx(A, B) / (mx(A, B) / i) + 1)
				if (ok(i)) {
					c = i, C = ok(i);
					break;
				}
		}
		else {
			if (ok(a + 1))
				c = a + 1, C = ok(a + 1);
			if (ok(b + 1))
				c = b + 1, C = ok(b + 1);
		}
		if (c)
			print(c), putchar(' '), print(C), putchar('\n');
		else
			puts("-1 -1");
	}
}

八仙敬酒

吕洞宾——醉酒提壶力千钧 description ⇐ \Leftarrow 难度系数 4.0 4.0 4.0
铁拐李——旋肘膝撞醉还真 description ⇐ \Leftarrow 难度系数 3.5 3.5 3.5
汉钟离——跌步抱坛兜心顶 description ⇐ \Leftarrow 难度系数 3.0 3.0 3.0 Appeared in LZOI-1,主题库 Link ⇐ \Leftarrow
蓝采和——单提敬酒拦腰破 description ⇐ \Leftarrow 难度系数 3.5 3.5 3.5 Appeared in DCOI-2021,主题库 Link ⇐ \Leftarrow
张果老——醉酒抛杯踢连环 description ⇐ \Leftarrow
曹国舅——仙人敬酒锁喉扣 description ⇐ \Leftarrow
韩湘子——擒腕击胸醉吹箫 description ⇐ \Leftarrow
何仙姑——弹腰献酒醉荡步 description ⇐ \Leftarrow
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您好!要使用Python编写爬虫来获取携程游记的信息,您可以按照以下步骤进行操作: 1. 导入所需的库:您可以使用`requests`库来发送HTTP请求,使用`BeautifulSoup`库来解析HTML页面。 2. 发送HTTP请求并获取页面内容:使用`requests`库发送GET请求,获取携程游记的页面内容。 3. 解析HTML页面:使用`BeautifulSoup`库解析页面内容,提取出游记的相关信息。 4. 提取游记信息:根据页面的HTML结构,使用`BeautifulSoup`库提取游记的标题、作者、发布时间、内容等信息。 5. 存储游记信息:您可以选择将提取到的游记信息存储到文件或数据库中,以便后续使用或分析。 以下是一个简单的示例代码,以帮助您开始编写爬虫: ```python import requests from bs4 import BeautifulSoup # 发送HTTP请求并获取页面内容 url = 'https://you.ctrip.com/travels' response = requests.get(url) html_content = response.text # 解析HTML页面 soup = BeautifulSoup(html_content, 'html.parser') # 提取游记信息 travel_list = soup.find_all('div', class_='journals_list') for travel in travel_list: title = travel.find('h2').text author = travel.find('span', class_='username').text publish_time = travel.find('span', class_='time').text content = travel.find('div', class_='ctd_content').text # 打印游记信息 print('标题:', title) print('作者:', author) print('发布时间:', publish_time) print('内容:', content) print('---') ``` 请注意,这只是一个简单的示例代码,具体的网页结构可能会有所变化,您可能需要根据实际情况做一些调整。此外,爬取网页内容时请尊重网站的规则和条款,并遵循合适的爬取频率,以免对网站造成过大的负担。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值