【 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 【LGR−095】洛谷 10 月月赛 III &「LZOI」Round ⋅ 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 k−k×⌊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 1⋯2k 这 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 1⋯7 号结点的中点: 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 1↔2↔3↔4↔5↔6↔7
8 ↔ 3 ↔ 9 8\leftrightarrow\color{red}3\color{black}\leftrightarrow9 8↔3↔9
其中 3 \color{red}3 3 是树的重心, 4 \color{blue}4 4 是直径重心,上面的这一条链就是直径。
三、N >= 10 情况的讨论
不难发现,只要我们继续按照该策略,不断地给结点 3 3 3 添加新的子结点(形成一张菊花图)即可。
四、代码实现
当
N
≤
8
N\le8
N≤8 时:输出 -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 1↔2↔3↔4↔5↔6↔7
这个基本模型,然后对于 i = 8 ∼ N i=8\sim N i=8∼N 的所有结点 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) (i−1,j,2),(i+1,j,0) | ( i − 1 , j , 2 ) (i-1,j,2) (i−1,j,2) | ( i − 1 , j , 2 ) , ( i + 1 , j , 0 ) (i-1,j,2),(i+1,j,0) (i−1,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,j−1,1),(i,j+1,3) | ( i , j − 1 , 1 ) (i,j-1,1) (i,j−1,1) | ( i , j − 1 , 1 ) , ( i , j + 1 , 3 ) (i,j-1,1),(i,j+1,3) (i,j−1,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,j−1,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) (i−1,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) (i−1,j,2) | ( i , j − 1 , 1 ) (i,j-1,1) (i,j−1,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,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,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 , 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,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,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) |
< | d d d | ( i , j − 1 , 1 ) (i,j-1,1) (i,j−1,1) | ( i , j − 2 , 1 ) \color{red}(i,j-2,1) (i,j−2,1) | ( i , j − 1 , 1 ) (i,j-1,1) (i,j−1,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) (i−1,j,2) | ( i − 2 , j , 2 ) \color{red}(i-2,j,2) (i−2,j,2) | ( i − 1 , j , 2 ) (i-1,j,2) (i−1,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,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,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 , 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,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,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) |
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 (M−1)2,考虑优化:
-
C \texttt{C} C 只需要能击败 B \texttt{B} B,因此 c c c 的枚举上限应为 max ( 1 + b , B ) \max(1+b,B) max(1+b,B)。
-
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(3MlogM) 的做法)
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+a−1
据此就可以 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⌉=⌊qp−1⌋+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");
}
}