每次动态更新上下界,最后二分判断一下界内的被删除数字即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 #include <bits/stdc++.h> using namespace std;const int N = 2e5 + 10 ;const int inf = 1 << 30 ;const long long llinf = 1ll << 60 ;const double PI = acos (-1 );#define lowbit(x) (x&-x) typedef long long ll;typedef double db;typedef pair<int , int > pii;typedef pair<ll, ll> pll;typedef pair<db, db> pdd;typedef pair<ll, int > pli;int n, m, k, q;void work () { cin >> n; ll l = 0 , r = 1e10 , x; vector<int > vec; for (int i = 1 , op; i <= n; i++) { cin >> op >> x; switch (op) { case 1 : { l = max (x, l); break ; } case 2 : { r = min (x, r); break ; } case 3 : { vec.push_back (x); break ; } } } if (l > r) { cout << 0 << endl; return ; } sort (vec.begin (), vec.end ()); unique (vec.begin (), vec.end ()); auto it1 = lower_bound (vec.begin (), vec.end (), l); auto it2 = --upper_bound (vec.begin (), vec.end (), r); if (it2 - it1 < 0 || (*it1) < l || (*it2) > r) { cout << r - l + 1 << endl; } else { cout << r - l + 1ll - (ll)(it2 - it1 + 1 ) << endl; } } int main () { ios::sync_with_stdio (0 ); cin.tie (0 ); cout.tie (0 ); int t; cin >> t; while (t --> 0 ) { work (); } }
对后手而言,显然把最大的\(x\) 个数取反了是最优的。所以作为先手,我们枚举去掉最大的\(1\dots
k\) 个数,最终结果是多少,排序+前缀和即可,注意判断边界。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 #include <bits/stdc++.h> using namespace std;const int N = 2e5 + 10 ;const int inf = 1 << 30 ;const long long llinf = 1ll << 60 ;const double PI = acos (-1 );#define lowbit(x) (x&-x) typedef long long ll;typedef double db;typedef pair<int , int > pii;typedef pair<ll, ll> pll;typedef pair<db, db> pdd;typedef pair<ll, int > pli;ll a[N]; void work () { int n, k, x; cin >> n >> k >> x; for (int i = 1 ; i <= n; i++) { cin >> a[i]; } sort (a + 1 , a + n + 1 ); ll val = -1e10 ; for (int i = 2 ; i <= n; i++) a[i] += a[i - 1 ]; if (n == k) val = 0 ; for (int i = 0 ; i <= k; i++) { if (n - i < 0 ) break ; val = max (val, 2ll * a[max (0 , n - i - x)] - a[n - i]); } cout << val << endl; } int main () { ios::sync_with_stdio (0 ); cin.tie (0 ); cout.tie (0 ); int t; cin >> t; while (t --> 0 ) { work (); } }
题意可知,对于每个整除n的\(k\) ,每一个下标组 \[
\begin{gather}
\{1,1+k,1+2k\dots 1+(\frac n k -1)k\}, \\\\
\{2,2+k,2+2k\dots 2+(\frac n k -1)k\}, \\\\
\dots \\\\
\{k,2k\dots n\}
\end{gather}
\] 组内的元素对某个数\(m\) 同余。
观察题目可以发现,我们实际上并不关心\(m\) 是多少,只需知道存不存在这样的\(m\) 。
对于这样一个式子: \[
\begin{align}
a \equiv b\pmod M \\\\
b \equiv c\pmod M \\\\
\end{align}
\] 移项得: \[
\begin{align}
a-b \equiv 0\pmod M \\\\
b-c \equiv 0\pmod M \\\\
\end{align}
\] 等效于: \[
gcd(a -b,b-c) \not= 1
\]
可知,若一组数存在这样一个\(M\) ,那么其相邻数的差的\(gcd\) 一定不为1。
枚举每个\(k\) ,然后将每组下标提取出来,判断即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #include <bits/stdc++.h> using namespace std;const int N = 2e5 + 10 ;const int inf = 1 << 30 ;const long long llinf = 1ll << 60 ;const double PI = acos (-1 );#define lowbit(x) (x&-x) typedef long long ll;typedef double db;typedef pair<int , int > pii;typedef pair<ll, ll> pll;typedef pair<db, db> pdd;typedef pair<ll, int > pli;int n, m, k, q;int a[N];void work () { cin >> n; for (int i = 1 ; i <= n; i++) cin >> a[i]; auto calc = [&](int k) { int val = 0 ; for (int i = 1 ; i <= k; i++) { vector<int > b; for (int pos = i; pos <= n; pos += k) b.push_back (a[pos]); sort (b.begin (), b.end ()); for (int i = 1 ; i < b.size (); i++) val = __gcd(val, b[i] - b[i - 1 ]); } return val != 1 ; }; int ans = 0 ; for (int k = 1 ; k * k <= n; k++) if (n % k == 0 ) ans += calc (k); for (int k = n; (n / k) * (n / k) < n; k--) if (n % k == 0 ) ans += calc (k); cout << ans << endl; } int main () { ios::sync_with_stdio (0 ); cin.tie (0 ); cout.tie (0 ); int t; cin >> t; while (t --> 0 ) { work (); } }
查询的下标很大(1e18),不可能构造出实际数列,考虑其他做法。
每次1操作只会在末尾加上一个新数字,说明有效数字较少,长度的增加主要来自于2操作的增倍。
先模拟生成数列的过程,记录下操作到当前步骤时的数列长度 以及增加数字 (操作1)或复制次数 (操作2),按照数列长度排序。
若现在要查询下标\(pos\) ,二分找到比\(pos\) 大的最小长度对应的操作。
若是操作1,则增加的数字显然就是\(pos\) 对应的数值。
若是操作2,设增倍后,长度变为\(X\) 倍(题目中是复制\(x\) 次,即有\(X=x+1\) ),长度为\(L\) ,则等效于在查找\((pos+ 1)\bmod X -1\) 下标的数字。
容易发现,每经过这样一次取模操作,数据规模都减小至少一半,即单次复杂度小于\(log_{2}^2Q_i\) ,总复杂度为\(\Theta(Qlog_2^2{(q_{max})})\)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 #include <bits/stdc++.h> using namespace std;const int N = 2e5 + 10 ;const int inf = 1 << 30 ;const long long llinf = 1ll << 60 ;const double PI = acos (-1 );#define lowbit(x) (x&-x) typedef long long ll;typedef double db;typedef pair<int , int > pii;typedef pair<ll, ll> pll;typedef pair<db, db> pdd;typedef pair<ll, int > pli;typedef pair<__int128, __int128> p128;struct Node { int opt; __int128 pos, x; Node (int a, __int128 b, __int128 c) : opt (a), pos (b), x (c) {} bool operator < (const Node b) { return pos < b.pos; } }; int n, m, k, q;int a[N], cnt;vector<Node> vec; map<__int128, int > f; int search (__int128 pos) { if (f.find (pos) != f.end ()) return f[pos]; auto it = lower_bound (vec.begin (), vec.end (), Node (0 , pos, 0 )); if (it->opt == 1 && it->pos == pos) return a[it->x]; __int128 new_pos = (__int128)((pos - 1 ) % it->x + 1ll ); return f[pos] = search (new_pos); } void work () { cin >> n >> k; __int128 len = 0 ; vec.clear (); f.clear (); for (int i = 1 , opt; i <= n; i++) { ll x; cin >> opt >> x; if (len > (__int128)(1e18 )) continue ; if (opt == 1 ) { len++; vec.push_back (Node (opt, len, ++cnt)); a[cnt] = x; } else { __int128 bk = len; len *= (x + 1ll ); vec.push_back (Node (opt, len, bk)); } } ll x; for (int i = 1 ; i <= k; i++) { cin >> x; cout << search (x) << ' ' << flush; } cout << endl; } int main () { ios::sync_with_stdio (0 ); cin.tie (0 ); cout.tie (0 ); int t; cin >> t; while (t --> 0 ) { work (); } }
考虑dp。
设计状态dp[i][j]
表示在有\(i\) 个仅含1个1的子串、最后一个1前有\(j\) 个0、以1结尾的字符串个数。
状态确定后,大致的转移方程也可以很快写出来: \[
dp[i][j] = \sum{dp[i-cnt][k]} \\
\]
其中,cnt
表示从上一个状态转移到下一个状态后,增加了多少个符合要求的子串。
接下来考虑如何计算cnt
以及判断边界。
考虑这样的串: \[
\underbrace{0,0\dots 0}_{k个0},1
\] 当在后面加上\(j\) 个0、1个1后: \[
\underbrace{0,0 \dots 0}_{k个0},1,\underbrace{0,0 \dots 0}_{j个0},1
\] 增加的满足要求子串分为四部分:
一部分是前面的1到后面的0构成的子串,有\(j\) 个;
第二部分是前面的\(k\) 个0到后面的\(j\) 个0构成的子串,有\(j*k\) 个;
第三部分是后面\(j\) 个0与后面的1构成的子串,有\(j\) 个;
最后一部分是增加的1本身,有1个。
所以, \[
\begin{align}
cnt & =j+j+j*k+1 \\\\
& =j*(k+2)+1 \\\\
\end{align}
\]
然后考虑边界条件。
最大长度不能超过\(lim\) (即输入中的k),所以有 \[
j+k+1 \leq lim
\] 增加的子串数量显然不能超过当前状态的子串数量,有 \[
cnt\leq i
\] 这样就完成了主要部分的转移方程。
但是,可以发现,开头部分的转移并没有1在最前面,因此需要特判;结尾部分也要处理结尾不为0的部分,统计答案的时候也要单独处理,详细的可以看代码理解。
时间复杂度不会分析,反正是\(\Theta(能过)\) 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 #include <bits/stdc++.h> using namespace std;const int N = 2e5 + 10 ;const int inf = 1 << 30 ;const long long llinf = 1ll << 60 ;const double PI = acos (-1 );#define lowbit(x) (x&-x) typedef long long ll;typedef double db;typedef pair<int , int > pii;typedef pair<ll, ll> pll;typedef pair<db, db> pdd;typedef pair<ll, int > pli;int n, m, k, q, lim;ll f[2510 ][2510 ]; const ll mod = 998244353ll ;void work () { cin >> n >> lim; for (int i = 0 ; i <= n; i++) for (int j = 0 ; j <= n; j++) f[i][j] = 0 ; for (int j = 0 ; j <= n; j++) { if (j + 1 > lim) break ; f[j + 1 ][j] = 1 ; } for (int i = 1 ; i <= n + 1 ; i++) { for (int j = 0 ; j < lim; j++) { if (j > i) break ; for (int k = 0 ; k <= i; k++) { if (j + k + 1 > lim) break ; int cnt = j * k + 2 * j + 1 ; if (i < cnt) break ; f[i][j] = (f[i][j] + f[i - cnt][k]) % mod; } } } bool DEBUG = false ; if (DEBUG) { cout << " " ; for (int i = 0 ; i <= n; i++) cout << i << ' ' ; cout << endl; for (int i = 0 ; i <= n; i++) { cout << i << "| " ; for (int j = 0 ; j <= n; j++) cout << f[i][j] << ' ' ; cout << endl; } } ll ans = 0 ; for (int j = 0 ; j <= n; j++) { for (int k = 0 ; k <= n; k++) { int cnt = j * k + j; if (j + k + 1 > lim) break ; if (n <= cnt) break ; ans = (ans + f[n - cnt][k]) % mod; if (DEBUG && f[n - cnt][k] != 0 ) cout << j << ' ' << k << ' ' << n - cnt << ' ' << f[n - cnt][k] << endl; } } cout << ans << endl; } int main () { ios::sync_with_stdio (0 ); cin.tie (0 ); cout.tie (0 ); int t; cin >> t; while (t --> 0 ) { work (); } }
要求求路径到火山最小距离的最大值,似乎可以二分?
于是考虑二分到火山距离的最小值\(x\) 。然后,我们直接从\((x,y)\) 出发,暴力\(\text{BFS}\) 把所有离火山距离$ x\(的**四联通**点全部染上,点到火山的距离可以用类似\) \(的做法在\) n2\(的时间内预处理出来。然后,再暴力从火山出发**八联通扩展**,看能否从火山扩展到边界。这样就能够非常暴力地完成这题。时间复杂度\) (qn 2log_2n)$,显然能通过此题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 #include <bits/stdc++.h> using namespace std; const int N = 2e5 + 10 ;const int inf = 1 << 30 ;const long long llinf = 1ll << 60 ;const double PI = acos (-1 ); #define lowbit(x) (x&-x) typedef long long ll;typedef double db;typedef pair<int , int > pii;typedef pair<ll, ll> pll;typedef pair<db, db> pdd;typedef pair<ll, int > pli; int n, m, k, q;void work () { cin >> n >> m >> q; vector< vector<int > > a (n + 2 , vector <int >(m + 2 )); char ch; for (int i = 1 ; i <= n; i++) for (int j = 1 ; j <= m; j++) { cin >> ch; switch (ch) { case '.' : a[i][j] = 0 ; break ; case '#' : a[i][j] = 1 ; break ; case 'v' : a[i][j] = 2 ; break ; } } vector< vector<int > > f (n + 2 , vector <int >(m + 2 , inf)); auto process = [&]() { #define min(x,y,z) min(x, min(y, z)) for (int i = 1 ; i <= n; i++) for (int j = 1 ; j <= m; j++) if (a[i][j] == 2 ) f[i][j] = 0 ; else f[i][j] = min (f[i][j], f[i - 1 ][j] + 1 , f[i][j - 1 ] + 1 ); for (int i = n; i >= 1 ; i--) for (int j = 1 ; j <= m; j++) if (a[i][j] == 2 ) f[i][j] = 0 ; else f[i][j] = min (f[i][j], f[i + 1 ][j] + 1 , f[i][j - 1 ] + 1 ); for (int i = 1 ; i <= n; i++) for (int j = m; j >= 1 ; j--) if (a[i][j] == 2 ) f[i][j] = 0 ; else f[i][j] = min (f[i][j], f[i - 1 ][j] + 1 , f[i][j + 1 ] + 1 ); for (int i = n; i >= 1 ; i--) for (int j = m; j >= 1 ; j--) if (a[i][j] == 2 ) f[i][j] = 0 ; else f[i][j] = min (f[i][j], f[i + 1 ][j] + 1 , f[i][j + 1 ] + 1 ); }; process (); process (); #define __DEBUG__ false auto check = [&](int lim, pii pos) { vector< vector<int > > b (n + 2 , vector <int >(m + 2 , 0 )); queue<pii> q1; q1.push (pos); const pii dir[] = {make_pair (-1 , 0 ), make_pair (1 , 0 ), make_pair (0 , -1 ), make_pair (0 , 1 )}; b[pos.first][pos.second] = 1 ; while (!q1.empty ()) { auto [x, y] = q1.front (); q1.pop (); for (auto [dx, dy] : dir) { if (x + dx < 1 || x + dx > n || y + dy < 1 || y + dy > m) continue ; if (f[x + dx][y + dy] >= lim && !b[x + dx][y + dy] && a[x + dx][y + dy] == 0 ) { b[x + dx][y + dy] = 1 ; q1.push ({x + dx, y + dy}); } } } if (__DEBUG__) { for (int i = 1 ; i <= n; i++) for (int j = 1 ; j <= m; j++) cout << b[i][j] << " \n" [j == m]; cout << endl; } queue<pii> q; const pii dirs[] = {make_pair (-1 , 0 ), make_pair (1 , 0 ), make_pair (0 , -1 ), make_pair (0 , 1 ), make_pair (-1 , -1 ), make_pair (1 , 1 ), make_pair (1 , -1 ), make_pair (-1 , 1 )}; for (int i = 1 ; i <= n; i++) for (int j = 1 ; j <= m; j++) if (a[i][j] == 1 ) q.push ({i, j}); while (!q.empty ()) { auto [x, y] = q.front (); q.pop (); if (x == 1 || x == n || y == 1 || y == m) return false ; for (auto [dx, dy] : dirs) { if (b[x + dx][y + dy] == 0 ) { b[x + dx][y + dy] = 2 ; q.push ({x + dx, y + dy}); } } } return true ; }; for (int i = 1 ; i <= q; i++) { int x, y; cin >> x >> y; int l = 0 , r = f[x][y], mid, ans = 0 ; while (l <= r) { mid = (l + r) / 2 ; if (check (mid, {x, y})) { l = mid + 1 ; ans = mid; } else r = mid - 1 ; } cout << ans << endl; } } int main () { ios::sync_with_stdio (0 ); cin.tie (0 ); cout.tie (0 ); int t = 1 ; while (t --> 0 ) { work (); } }