步梯

Elgina發表於2024-08-10

補題
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;
}

相關文章