補題
int: 2147483647 = \(2 \times 10 ^ 9\)
牛客第一場 A
\({a_1,a_2,...,a_n}\) 其中 \(a_i\) 值域 小於 \(2 ^ m\) ,問有多少種子序列 方案數 使得 AND 值 為 1
首先選擇 \(C_n^{k}\) 個 組成 AND 值為1 的 數 , 他們有共同點 二進位制下最後一位均是1 , 還要保證 一共\(m-1\)位情況下, 每位對應的一列 \(k\)個,不全為1 ,共 \(2^k -1\) 種,(\(0000 - 1110\)) ,再 \(m - 1\) 次冪。
未選擇參與構成的元素 有以下特點: 最後一位必須為0, 其他位無要求, \(2 ^ {(m-1) \times (n - k)}\)
\(k:\) \(C_n ^{k} \times (2^k - 1) ^{m-1} \times 2^{(m-1)\times(n-k)}\)
美劇\(k\) 累加即可 建議列表發現組合數學規律
#include <bits/stdc++.h>
typedef long long ll;
void solve() {
int n,m,p;
std::cin >> n >> m >> p;
auto ksm = [&](ll a, ll b,ll p) {
ll res = 1;
while(b) {
if(b & 1) res = res * a % p;
a = a * a % p;
b >>= 1;
}
return res;
};
std::vector<std::array<ll,5002>> c(n+1);
for(int i = 0; i <= n; i++) c[i][0] = 1;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= i; j++) {
c[i][j] = c[i-1][j] + c[i-1][j-1];
c[i][j] %= p;
}
}
ll ans = 0;
for(int k = 1; k <= n; k++) {
ans = ans + c[n][k] * ksm(2,(m-1)*(n-k),p) %p * ksm( ksm(2,k-1,p) , m-1 , p) % p;
ans = (ans + p) % p;
}
std::cout << ans << '\n';
return;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int _ = 1;
//std::cin >> _;
while(_ --) {
solve();
}
return 0;
}
$ div3 D $
\(n,x \le 10^6\) $a\times b + b \times c + c \times a \le n \quad a + b + c \le x $ 求正整數三元組\({a,b,c}\) 數量
就是美劇
#include <bits/stdc++.h>
typedef long long ll;
const int N = 2e6 + 50;
void solve()
{
int n,x;
std::cin >> n >> x;
ll ans = 0;
for(int a = 1; a <= n; a ++) {
for(int b = 1; a * b <= n && a + b <= x; b ++)
ans += std::min((n - a * b) / (a + b), x - (a + b));
}
std::cout << ans << '\n';
return;
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int _ = 1;
std::cin >> _ ;
while(_ --) {
solve();
}
return 0;
}
\(div3 C\)
\(A,B\) 兩串 ,看最少修改幾次 能夠使給定區間 \([l,r]\) 內元素 排序後相同
明顯上下相同 不用改
特殊資料 \(aaaa/bbbb\)
美劇每個字元 在該區間內的上下差距數 ,累加
#include <bits/stdc++.h>
typedef long long ll;
const int N = 2e6 + 50;
void solve()
{
int n,q;
std::cin >> n >> q;
std::vector<std::array<int,26>> pre(n+1);
std::string a,b;
std::cin >> a >> b;
for(int i = 1; i <= n; i ++) {
pre[i] = pre[i-1];
pre[i][a[i-1] - 'a'] ++;
pre[i][b[i-1] - 'a'] --;
}
while(q --) {
int l,r;
ll ans = 0;
std::cin >> l >> r;
for(int c = 0; c < 26; c ++) {
ans += std::max(0,pre[r][c] - pre[l-1][c]);
}
std::cout << ans << '\n';
}
return;
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int _ = 1;
std::cin >> _ ;
while(_ --) {
solve();
}
return 0;
}
\(hdu 3 \quad1007\)
區間加 ,區間判斷 相等, 單調遞增 ,單調遞減 , 單峰數列
哥哥用的方法是差分 ,將 每個位置\(i\) 的差分正負作為加入 \(set\) 的依據
若 正 和 零 的集合內沒有位置 說明單調遞減
類比 注意 單峰數列 首先不能平 ,還要保證嚴格單增後單減
雖然這題很難碰到類似的 但是學習到了內建函式的設定方法
#include <bits/stdc++.h>
typedef long long ll;
void solve() {
int n;
std::cin >> n;
std::vector<ll> a(n),d(n);
for(int i = 0; i < n; i++) {
std::cin >> a[i];
}
for(int i = 1; i < n; i++) {
d[i] = a[i] - a[i-1];
}
std::set<int> Z,P,N;
auto work = [&](int i) {
Z.erase(i);
P.erase(i);
N.erase(i);
if(d[i] > 0) P.insert(i);// +
else if(d[i] < 0) N.insert(i);// -
else Z.insert(i);// 0
};
for(int i = 1; i < n; i++) work(i);
auto query = [&](int o,int l,int r) {
if(o == 2) {
return P.upper_bound(l) == P.lower_bound(r) && N.upper_bound(l) == N.lower_bound(r);
} else if(o == 3) {
return Z.upper_bound(l) == Z.lower_bound(r) && N.upper_bound(l) == N.lower_bound(r);
} else if(o == 4) {
return P.upper_bound(l) == P.lower_bound(r) && Z.upper_bound(l) == Z.lower_bound(r);
} else if(o == 5) {
if(Z.upper_bound(l) != Z.lower_bound(r))
return false;
auto pl = P.upper_bound(l);
auto pr = P.lower_bound(r);
auto nl = N.upper_bound(l);
auto nr = N.lower_bound(r);
if(pl == pr || nl == nr) return false;
return *std::prev(pr) < *nl;
}
};
int q;
std::cin >> q;
while(q --) {
int o,l,r;
std::cin >> o >> l >> r;
l --;
if(o == 1) {
int x;
std::cin >> x;
if(l > 0) {
d[l] += x;
work(l);
}
if(r < n) {
d[r] -= x;
work(r);
}
}
else {
std::cout << query(o,l,r) << '\n';
}
}
return;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int _ = 1;
//std::cin >> _;
while(_ --) {
solve();
}
return 0;
}
\(abc D\)
\(x\) 軸上 有 \(n\) 個點 , 求給定點\(b[i]\) 在該軸上距離第\(k[i]\) 近的點 與 \(b[i]\) 的(最近)距離
二分答案 距離 看看該點 向左右延伸的距離能否包含 \(k[i]\)個 點
#include <bits/stdc++.h>
typedef long long ll;
const int N = 2e6 + 50;
void solve()
{
int n,q;
std::cin >> n >> q;
std::vector<int> a(n), b(q),k(q);
for(int i = 0; i < n; i ++) std::cin >> a[i];
std::sort(a.begin(), a.end());
for(int i = 0; i < q; i ++) {
std::cin >> b[i] >> k[i];
int l = 0, r = 2e8, x = 0;
while(l <= r) {
int mid = (l + r) >> 1;
int ml = b[i] - mid, mr = b[i] + mid;
int cnt = std::upper_bound(a.begin(), a.end(), mr) - std::lower_bound(a.begin(), a.end(), ml);
if(cnt >= k[i]) {
x = mid;
r = mid - 1;
}
else l = mid + 1;
}
std::cout << x << '\n';
}
return;
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int _ = 1;
//std::cin >> _ ;
while(_ --) {
solve();
}
return 0;
}
\(dp\quad + \quad bitset\)
一共有\(n\)個數,第\(i\)個數是\(x_i\), \(x_i\) 可以取 \([l_i,r_i]\) 中任意的一個值。設 \(S = \sum{{x_i}^2}\),求 S 種類數。
bitset<N> f//相當於bool f[N]
f.any()//整個二進位制中是否有一個位置被置為1
f.none()//是否整個f中所有位上都是0
f.count()//二進位制中1的個數
f.set()//將二進位制各個位置全都置為1
f.set(i)//相當於f[i]=1
f.reset()//將二進位制各個位置全都置為0
f.reset(i)//相當於f[i]=0
f.flip()//將二進位制所有位取反
f.flip(i)//相當於f[i]^=1
透過\(STL\)中的\(bitset\)去最佳化。
比如 11001 表示 1 4 5 可以用加法得到,因為 位置1 4 5上的數為1
總結一下,\(bitset\)的第X位為1表示整數X可以透過加法得到。
\(ans[i][j]\) 表示 第\(i\)行 能不能出現 \(j\) 這個值 存的是長度為 N 的01串
#include <bits/stdc++.h>
typedef long long ll;
const int N = 1e6 + 50;
void solve() {
int n;
std::cin >> n;
std::bitset<N> ans[101];
ans[0][0] = 1;
for(int i = 1,l,r; i <= n; i++) {
std::cin >> l >> r;
for(int j = l; j <= r; j++) {
ans[i] |= ans[i-1] << j * j;
}
}
std::cout << ans[n].count() << '\n';
return;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int _ = 1;
//std::cin >> _;
while(_ --) {
solve();
}
return 0;
}
\(D\)
\(\,\,\,\,\,\,\,\,\,\,\)小紅希望出一場題目,但是他的實力又不夠,所以他想到可以從以前的比賽中各抽一題,來組成一場比賽。不過一場比賽的難度應該是有限制的,所以所以這一場比賽會給一個目標難度分數 \(\rm target\) 。
\(\,\,\,\,\,\,\,\,\,\,\)小紅選 \(n\) 場比賽,每場 \(m\) 個題,小紅會從每一場選一道題,使其組成的題的難度分數儘量接近 \(\rm target\) 。小紅想知道挑選的題的難度分數與 \(\rm target\) 相差的最小值是多少。
#include <bits/stdc++.h>
typedef long long ll;
const int N = 1e6 + 50;
int a[105][50];
void solve() {
int n,m;
std::cin >> n >> m;
std::bitset<N> ans[101];
ans[0][0] = 1;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++)
std::cin >> a[i][j];
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
ans[i] |= ans[i-1] << a[i][j];
}
}
int tar , res = N;
std::cin >> tar;
for(int i = 0; i <= 10000; i++) {
if(ans[n][i]) {
res = std::min(res,std::abs(tar - i));
}
}
std::cout << res << '\n';
return;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int _ = 1;
//std::cin >> _;
while(_ --) {
solve();
}
return 0;
}
\(E\)
\(\,\,\,\,\,\,\,\,\,\,\)已知長度為 \(n\) 的序列 \(a_1,a_2,\dots,a_n\) ,定義一次操作的過程為:選擇任意一個元素,隨後,將 \(\left \lfloor \dfrac{a_i}{2} \right \rfloor\)(向下取整)新增到原序列的結尾,並將 \(a_i\) 從原序列中刪除。
\(\,\,\,\,\,\,\,\,\,\,\)你可以進行任意多次操作(也可以一次操作都不做),要求使得序列的 \(\rm MEX\) 最大。
\(\,\,\,\,\,\,\,\,\,\,\)陣列的 \(\rm MEX\) 定義為:沒有出現在陣列中的最小非負整數,例如,陣列 \(\{3,1,2\}\) 的 \(\rm MEX\) 為 \(0\) 。
二分最後能達成的值 \(O(nlogn)\)
#include <bits/stdc++.h>
typedef long long ll;
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n),b(n);
for(int i = 0; i < n; i++) std::cin >> a[i];
auto check = [&] (int t) {
b = a;
std::set<int> s;
for(int i = 0; i < n; i++) {
while(b[i] > t) b[i] /= 2;
while(s.count(b[i]) && b[i] > 0) b[i] /= 2;
s.insert(b[i]);
}
return (s.size() == (t + 1));
};
int l = 0, r = n + 1, x = 0;
while(l <= r) {
int mid = (l + r) >> 1;
if(check(mid)) {
l = mid + 1;
x = mid;
}
else r = mid - 1;
}
std::cout << x + 1 << '\n';
return;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int _ = 1;
std::cin >> _;
while(_ --) {
solve();
}
return 0;
}
\(1992 E\)
\(num = n \times a - b\)
\(i = len \times a - b\)
\(a \le 10000 \quad len \le 2\) 因此 $i 是個位數 $ 列舉 注意\(n = 1\)
#include <bits/stdc++.h>
typedef long long ll;
void solve() {
int n;
std::cin >> n;
if(n == 1) {
std::cout << 9999 << '\n';
for(int i = 2; i <= 10000; i++) {
std::cout << i << " " << i - 1<< '\n';
}
return;
}
std::string s = std::to_string(n),o = s;
for(int i = 1; i <= 7; i++) s = s + o; // 10101010101010
std::set<std::pair<int,int>> se;
for(int i = 1; i <= 7; i++) {
std::string c = s.substr(0,i);// 10101
int num = 0;
for(int j = 0; j < i; j++) {
num = num * 10 + (c[j] - '0');
}
int p = n - o.length();
int q = num - i;
if(p == 0) continue;
if(q % p == 0 && p != 0){
int A = q / p;
int B = n * A - num;
if(A != 0 && B != 0 && A <= 10000) se.insert({A,B});
}
}
std::cout << se.size() << '\n';
for(auto [a,b] : se) {
std::cout << a << " " << b << '\n';
}
return;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int _ = 1;
std::cin >> _;
while(_ --) {
solve();
}
return 0;
}
\(河南 3 E\)
在一個長度為\(n\)的紙帶上,初始時所有位置顏色為白色,現在要執行以下兩種操作一共\(q\)次
操作一:輸入一個下標\(x\),你需要將位置\(x\)的顏色翻轉(白色變為黑色,黑色變為白色)
操作二; 輸入兩個正整數\(L , R\),你需要輸出區間[\([L,R]\)中的連續的白色區間長度最大值
#include <bits/stdc++.h>
typedef long long ll;
const int N = 2e6 + 50;
struct node{
int l,r,v;
int lmx,rmx,tmx; // 左 右 總
}t[N];
void pushup(node &u,node &l,node &r) {
u.lmx = std::max(l.lmx, l.lmx == l.r - l.l + 1?l.lmx + r.lmx:l.lmx);
u.rmx = std::max(r.rmx, r.rmx == r.r - r.l + 1?r.rmx + l.rmx:r.rmx);
u.tmx = std::max({l.tmx,r.tmx,l.rmx + r.lmx});
}
void push_up(int u) {
pushup(t[u],t[u<<1],t[u<<1|1]);
}
void build(int u,int l,int r) {
t[u].l = l; t[u].r = r;
if(l == r) {
t[u] = {l,r,0,1,1,1};
return;
}
int mid = (l + r) >> 1;
build(u << 1,l,mid);
build(u << 1 | 1,mid + 1,r);
push_up(u);
}
void modify(int u,int x) {
int l = t[u].l, r = t[u].r;
if(l == x && r == x) {
int pv = t[u].v ^ 1;
if(pv == 0) t[u] = {l,r,pv,1,1,1};
else t[u] = {l,r,pv,0,0,0};
return;
}
int mid = l + r >> 1;
if(x <= mid) modify(u << 1,x);
if(x > mid) modify(u << 1 | 1,x);
push_up(u);
}
node query(int u,int l,int r) {
if(l <= t[u].l && t[u].r <= r) return t[u];
int mid = t[u].l + t[u].r >> 1;
if(r <= mid) return query(u << 1,l,r);
else if(l > mid) return query(u << 1 | 1,l,r);
else {
node p,pl,pr;
pl = query(u << 1,l,r);
pr = query(u << 1 | 1,l,r);
pushup(p,pl,pr);
return p;
}
}
void solve() {
int n,q;
std::cin >> n >> q;
build(1,1,n);
for(int i = 1,op,x,y; i <= q; i++) {
std::cin >> op;
if(op == 1) {
std::cin >> x;
modify(1,x);
}
else {
std::cin >> x >> y;
std::cout << query(1,x,y).tmx << '\n';
}
}
return;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int _ = 1;
//std::cin >> _;
while(_ --) {
solve();
}
return 0;
}
\(牛客 2 E\)
\(gcd(x,y) = x \oplus y\) 給\(x\) , 求 任意\(y,y < x\)
\(....1000\) 若後面都是\(0\) ,表示能整除\(2^x\) ,若\(y\)的\(1\)前面與其相同,異或後消失, 能保證
\(....0000\) 能滿足要求, 只要\(x\) 減掉第一個一以及後面出現的所有即可, \(lowbit(x)\) 返回第一個1代表的二的冪次
#include <bits/stdc++.h>
typedef long long ll;
void solve() {
ll x;
std::cin >> x;
auto lowbit = [&] (ll i) {
return i & (-i);
};
if(x - lowbit(x) > 0) {
std::cout << x - lowbit(x) << '\n';
}
else std::cout << -1 << '\n';
return;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int _ = 1;
std::cin >> _;
while(_ --) {
solve();
}
return 0;
}
\(牛客2 C\)
紅色在一個 2⋅𝑛 的網格上,某些單元格是紅色的,其他單元格是白色的。
紅色可以最初選擇一個紅色單元格,並且在每一步中,可以選擇上方、下方、左側或右側的紅色單元格。當紅色離開一個單元格時,那個單元格會立刻變成白色。
紅色想知道她可以走的最大步數。
如果沒有初始紅色單元格,請輸出 0。
哥哥的從左到右正向\(dp\),
#include <bits/stdc++.h>
typedef long long ll;
void solve() {
int n,ans = 0;
std::cin >> n;
std::string s[2];
std::cin >> s[0] >> s[1];
int dp[2] = {};
for(int i = 0; i < n; i++) {
if(s[0][i] == 'R') dp[0]++;
else dp[0] = 0;
if(s[1][i] == 'R') dp[1]++;
else dp[1] = 0;
if(s[0][i] == 'R' && s[1][i] == 'R') {
int p = dp[0], q = dp[1];// 重複使用 防止更改
dp[0] = std::max(p,q + 1);
dp[1] = std::max(q,p + 1);//
}
ans = std::max({ans,dp[0],dp[1]});
}
std::cout << std::max(0,ans - 1);
return;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int _ = 1;
//std::cin >> _;
while(_ --) {
solve();
}
return 0;
}
\(牛客2H\)
更難的一道 \(dp\)
#include <bits/stdc++.h>
typedef long long ll;
// 題目要求經過 並非停留
void solve() {
int n,x,y;
std::cin >> n >> x >> y;
std::vector<std::array<int,2>> a(n+1);
a[0] = {0,0};
std::string s;
std::cin >> s;
for(int i = 0; i < n; i++) {
a[i+1] = a[i];
if(s[i] == 'W') a[i+1][1] ++;
else if(s[i] == 'S') a[i+1][1]--;
else if(s[i] == 'A') a[i+1][0]--;
else a[i+1][0]++;
}
ll ans = 0;
for(int i = 0; i <= n; i++) {
std::cout << a[i][0] << " " << a[i][1] << '\n'; // 字首位置
}
std::map<std::array<int,2>,int> mp;
for(int i = n; i >= 0; i--) {
mp[{a[i][0],a[i][1]}] = i;
if(mp.count({a[i][0] + x, a[i][1] + y})) {
int j = mp[{a[i][0] + x, a[i][1] + y}]; // j = i, x y = 0
j = std::max(j,i+1);//更新索引j,確保它是當前索引i和滿足條件的索引j中的較大值。
//std::cout <<i << ": " << j << " ";
ans += n - j + 1;
}
}
std::cout <<'\n' << ans << '\n';
return;
}
/*
0 0 x,y: 1,1 // -2,-1 + 1,1 = -1,0(r = 5) -> 3-5 3-6
-1 0 (i = 1)
-1 -1
-2 -1
-2 0
-1 0
0 0
3: 5 2: 6
3
*/
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int _ = 1;
//std::cin >> _;
while(_ --) {
solve();
}
return 0;
}
\(河南 3 G\)
現有三個正整數 \(A,B,C\),請你找三個整數\(x,y,z\)滿足\(x+y+z=n\),且\(0\leq x,y,z\),使得 \(|x*A + y*B + z*C - W|\) 最小。
為了簡化題目,現在只要找到\(|x*A + y*B + z*C - W|\)的最小值。
多組資料\(T\) , \(n\) 的和小於 \(10^6\)
如果需要求出單峰函式的極值點,通常使用二分法衍生出的三分法求單峰函式的極值點。
列舉 \(x\), \(y + z = n - x\) , 保留\(y\) 或 \(x\) , \(f(x) = x * A + y * B + (n - x - y) * C - w\)
由於絕對值, 先遞減後遞增 ,符合單峰函式, 時間複雜度 \(O(nlog n)\)
while (r - l > eps) { // eps 看題目 整數三分
mid = (l + r) / 2;
lmid = mid - eps;
rmid = mid + eps;
if (f(lmid) < f(rmid))
r = mid;
else
l = mid;
}
#include <bits/stdc++.h>
typedef long long ll;
void solve() {
ll A,B,C,n,W;
std::cin >> A >> B >> C >> n >> W;
ll ans = 1e18;
auto check = [&] (int x,int y) {
int z = n - x - y;
return std::abs(A * x + B * y + C * z - W);
};
for(int x = 0; x <= n; x ++) {
int l = 0, r = n - x;
while(r - l > 1) {
int mid = l + r >> 1;
int lmid = mid - 1;
int rmid = mid + 1;
if(check(x,lmid) < check(x,rmid)) r = mid;
else l = mid;
}
ans = std::min({ans,check(x,l),check(x,r)});
}
std::cout << ans << '\n';
return;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int _ = 1;
std::cin >> _;
while(_ --) {
solve();
}
return 0;
}
實數三分
double eps = 1e-9;
while(std::fabs(r - l) > eps) {
double m1 = (l * 2 + r) / 3;
double m2 = (l + r * 2) / 3;
if(check(m1) < check(m2)) r = m2;
else l = m1;
}
牛客2B
求樹 的 部分最小生成樹
由於資料多 用點度相對大小存邊
每次詢問的用時間戳標識, 表示不同批次
#include <bits/stdc++.h>
typedef long long ll;
const int N = 2e5 + 50;
struct E {
int u,v,w;
bool operator < (const E & x) const {
return w < x.w;
}
};
E e[N];
std::vector<std::pair<int,int>> G[N];
int d[N], tim = 0;
int fa[N],tag[N];
int find(int x) {
return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void solve() {
int k;
std::cin >> k;
tim ++;
std::vector<int> a(k+1);
for(int i = 1; i <= k; i++) {
std::cin >> a[i];
tag[a[i]] = tim;
fa[a[i]] = a[i];
}
if(k == 1) {
std::cout << 0 << '\n';
return;
}
std::vector<E> ve;
for(int i = 1; i <= k; i++) {
int x = a[i];
for(auto [v,w] : G[x]) {
if(tag[v] == tim) ve.push_back({x,v,w});
}
}
std::sort(ve.begin(),ve.end());
ll ans = 0, cnt = 0;
for(auto [u,v,w] : ve) {
u = find(u), v = find(v);
if(u == v) continue;
fa[v] = u;
ans += w; cnt += 1;
if(cnt == k - 1) {
std::cout << ans << '\n';
return;
}
}
std::cout << -1 << '\n';
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n,m,q;
std::cin >> n >> m >> q;
for(int i = 1; i <= m; i++) {
std::cin >> e[i].u >> e[i].v >> e[i].w;
d[e[i].u]++;
d[e[i].v]++;
}
for(int i = 1; i <= m; i++) {
int u = e[i].u, v = e[i].v;
if(d[u] < d[v] || d[u] == d[v] && u < v) G[u].push_back({v,e[i].w});
else G[v].push_back({u,e[i].w});
}
while(q --) {
solve();
}
return 0;
}
牛客3 B
給定\(n\)次操作, 每次可將\(x\) 修改為 \(|x-a_i|\)
求從\(D\) 開始可以得到的最小值。
$n \le 100, 1 \le a_i,D \le 10^{18} $
#include <bits/stdc++.h>
typedef long long ll;
ll gcd(ll a,ll b) {
if(b == 0) return a;
return gcd(b,a%b);
}
void solve() {
ll n,D;
std::cin >> n >> D;
std::vector<ll> h(n);
ll g = 0;
for(int i = 0; i < n; i++) {
std::cin >> h[i];
g = gcd(h[i],g);
}
std::cout << std::min(D % g, g - D % g) << '\n';
return;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int _ = 1;
//std::cin >> _;
while(_ --) {
solve();
}
return 0;
}
牛客3 A
一群\(n\)步行者在夜晚來到河邊。他們想用一艘船渡河,這艘船最初是在他們這邊的。這艘船一次最多能容納\(R\)名步行者,至少需要\(L\) \((1 \leq L < R)\)名步行者來操作。
划船是一項累人的工作。每次用船運送一群步行者到河的另一邊,船上所有步行者的耐力必須大於\(0\),每個步行者的耐力在旅程結束後會減少\(1\)。最初,\(i\)步行者\((1 \leq i \leq n)\)的耐力值為\(h_i\)。
你需要確定它是否可以運輸。
需要從河的右側往回送運趟數最小值:\(s = \left\lceil\dfrac{n-R}{R-L}\right\rceil\)
令$a_i = $ \(\left\lfloor\dfrac{h_i - 1}{2}\right\rfloor\) 為第\(i\)個人多來回的趟數
必要條件:\(\sum _{i=1} ^{n} min(a_i,s) \ge sL\)
貪心: 從右側往回運相當於每次將\(a_1,a_2,a_i,a_n\) 最大的 \(L\) 個元素減一
#include <bits/stdc++.h>
typedef long long ll;
void solve() {
int n,L,R;
int num = 0, ans = 0;
std::cin >> n >> L >> R;
int s = (n - R - 1) / (R - L) + 1;
std::vector<int> h(n),a(n);
for(int i = 0; i < n; i ++) {
std::cin >> h[i];
if(h[i] > 2) {
a[i] = (h[i] - 1) / 2;
num ++;
}
}
if(R >= n) {
std::cout << "Yes" << '\n';
return;
}
else {
if(num < L) std::cout << "No" << '\n';
else {
for(int i = 0; i < n; i++) {
ans += std::min(a[i],s);
}
if(ans >= s * L) {
std::cout << "Yes" << '\n';
}
else std::cout << "No" << '\n';
}
}
return;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int _ = 1;
//std::cin >> _;
while(_ --) {
solve();
}
return 0;
}