ICPC2023 杭州 題解

Martian148發表於2024-03-29

M - V-Diagram

Solution

很顯然,連續的子序列的一段肯定是包括最左邊或最右邊的其中一個點

Code

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const ll inf = 1ll << 60;
int main(){
    int t; cin >> t;
    while (t--) {
    	int n; cin >> n;
    	ll sum = 0;
        double ans = 0;
        vector<ll> a(n + 1),s(n + 1,0);
        for (int i = 1; i <= n; i++)
            cin >> a[i], s[i] = s[i - 1] + a[i];
        ll minn = inf; int idx = -1;
        for (int i = 1; i <= n; i++) 
            if (a[i] < minn) minn = a[i], idx = i;
        for (int i = 1; i < idx; i++) 
            ans = max(ans, 1.0 * (s[n] - s[i - 1]) / (n - i + 1));
        for (int i = idx + 1; i <= n; i++) 
            ans = max(ans, 1.0 * (s[i] - s[0]) / i);
        printf("%.10f\n", ans);
    }
    return 0;
} 

J - Mysterious Tree

Solution

互動題

首先,顯然需要找到一條連著的邊,我們可以用 \(\lceil \frac{n}{2}\rceil\) 個問來找到一條連著的邊

方法就是 \((1,2),(3,4),\cdots\)

如果都沒有連著的邊肯定是鏈了

考慮一條連著的邊,我們把兩個端點定義為 \(A,B\)

如果是菊花圖,考慮那個點是中心點,找到與 \(A,B\) 不同的兩個點 \(X,Y\)

如果 \(A\)\(X,Y\) 都有連邊,說明是菊花圖,且 \(A\) 是中心點

如果 \(B\)\(X,Y\) 都有連邊, 說明是菊花圖,且 \(B\) 是中心點

不滿足菊花圖的性質的就認為是鏈

Code

#include <bits/stdc++.h>
using namespace std;
void solve() {
    int n; cin >> n;
    int A = -1, B = -1;
    for (int i = 1; i <= n; i += 2) {
        int now_a = i, now_b = (i + 1 - 1) % n + 1;
        cout << '?' << ' ' << now_a << ' ' << now_b  << endl;
        int ok; cin >> ok;
        if (ok) {
            A = now_a, B = now_b;
            break;
        }
    }
    if (A == -1) {cout << '!' << ' ' << 1 << endl; return;} 
    int o1 = 1, o2 = 2;
    while (o1 == A || o1 == B) o1 = rand() % n + 1;
    while (o2 == A || o2 == B || o2 == o1) o2 = rand() % n + 1;
    cout << '?' << ' ' << A << ' ' << o1 << endl;
    int ok1; cin >> ok1;
    if (ok1) { // A is center
        cout << '?' << ' ' << A << ' ' << o2 << endl;
        int ok2; cin >> ok2;
        if (ok2) {cout << '!' << ' ' << 2 << endl; return;}
        else {cout << '!' << ' ' << 1 << endl; return;}
    }
    else { // B is center
        cout << '?' << ' ' << B << ' ' << o1 << endl;
        int ok3; cin >> ok3;
        if (ok3) {
            cout << '?' << ' ' << B << ' ' << o2 << endl;
            int ok4; cin >> ok4;
            if (ok4) {cout << '!' << ' ' << 2 << endl; return;}
            else {cout << '!' << ' ' << 1 << endl; return;}
        }
        else {cout << '!' << ' ' << 1 << endl; return;}
    }
}
int main() {
    srand(time(0));
    int t; cin >> t;
    while (t--) solve();
    return 0;
}

D - Operator Precedence

Solution

考慮到一個加和等於一個乘積

所以乘積中的很多項的絕對值應該為 \(1\)

不妨假定 \(a_i\) 對於所有的 \(i\) 為奇數的情況,為 \(a_i=1\)

除了 \(a_2\)\(a_n\) 之外,\(i\) 為偶數的情況為 \(a_i=-2\)

那麼我們就構造出了很多個 \(-1\)

然後假設 \(a_{2n}=1\) 反解 \(a_2\) ,如果解不出就定義 \(a_{2n} =2\) 反解 \(a_2\)

解得:

  • \(n\) 為奇數時,\(a_{2n}=1,a_2=(n-3)\)
  • \(n\) 為偶數時,\(a_{2n}=2,a_2=(n-2)\times (-2)\)

Code

#include <bits/stdc++.h>
using namespace std;
typedef  long long ll;
void solve() {
    int n; cin >> n;
    if (n == 2) {
        cout << "1 -3 -3 1" << '\n';
        return ;
    }
    if (n == 3) {
        cout << "1 -10 6 6 -10 1" << '\n';
        return ;
    }
    ll a2, an;
    if (n & 1) {
        a2 = (n - 2) - 1;
        an = 1;
    }
    else { // n 為偶數
        a2 = (n - 2) * (-2);
        an = 2;
    }
    for (int i = 1; i <= 2 * n; i++) {
        if (i & 1) printf ("1 ");
        else if (i == 2) printf ("%lld ", a2);
        else if (i == 2 * n) printf ("%lld\n", an);
        else printf ("-2 ");
    }
}
int main() {
    int t; cin >> t;
    while (t--) solve();
}

G - Snake Move

Solution

我們發現,蛇每走一步,無論是什麼操作,尾都會短一截

對於一個點來說,我們可以定義頭走到這個點所需要花費的最少步數 \(dis[x][y]\)

當這個點位於原來的蛇身上時,我們可以得出此時的蛇尾所在的位置,如果蛇頭和蛇身碰上的話,那麼需要多走幾步 \(S\) 操作,\(S\) 的操作步數就是 此時 蛇尾和 蛇頭的距離

我們發現蛇頭走一步所需要的代價是 \(1\) 但是走到蛇身所在的格子每次的代價可能 \(>1\)

我們借用 01BFS 的想法,把蛇身的多餘代價和位置放在一個優先佇列裡面

當列舉到與此時的步數相同的蛇身格子時,把這個格子放在佇列的前端處理

Code

#include <bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
typedef pair<int,int> pii;
typedef unsigned long long ull;
const int flg[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
int read() {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int read_ch() {
    char ch = getchar();
    while (ch != '.' && ch != '#') ch = getchar();
    return ch;
}

int main() {
    freopen ("G.in", "r", stdin);
    int N = read(), M = read(), K = read();
    vector<pii> node(1 + K);
    vector<vector<int> > mp(N + 1, vector<int>(M + 1, 0));
    for (int i = 1; i <= K; i++) {
        int x = read(), y = read();
        node[i] = {x, y};
    }
    for (int i = 1; i <= N; i++)
        for (int j = 1; j <= M; j++) {
            char ch = read_ch();
            if (ch == '#') mp[i][j] = -1;
        }
    for (int i = 1; i <= K; i++) {
        auto [x, y] = node[i];
        mp[x][y] = i;
    }

    priority_queue<pair<int, pii>, vector<pair<int, pii> >, greater<pair<int, pii> > > pq;
    vector<vector<int> > dis(N + 1, vector<int>(M + 1, inf));
    auto [sx, sy] = node[1];
    pq.push({0, {sx, sy}}); deque<pii> q;
    while (!pq.empty()) {

        auto get_node = [&] () -> bool{
            if (pq.empty()) return false;
            auto [d, u] = pq.top(); auto [x, y] = u;
            if (dis[x][y] != inf) { pq.pop(); return true;}
            auto [nx, ny] = q.front();
            if (q.empty() || d <= dis[nx][ny]) {
                pq.pop();
                dis[x][y] = d;
                q.push_front(u);
                return true;
            }
            return false;
        };

        while (get_node()) ;
        
        while (!q.empty()) {
            while (get_node()) ;
            auto [x, y] = q.front(); q.pop_front();
            for (int i = 0; i < 4; i++) {
                int nx = x + flg[i][0], ny = y + flg[i][1];
                if (nx < 1 || nx > N || ny < 1 || ny > M || mp[nx][ny] == -1 || dis[nx][ny] != inf) continue;
                if (mp[nx][ny] == 0) {
                    dis[nx][ny] = dis[x][y] + 1;
                    q.push_back({nx, ny});
                }
                else {
                    int d = dis[x][y] + 1 + max(0, K - dis[x][y] - mp[nx][ny]);
                    pq.push({d, {nx, ny}});
                }
            }
        }
    }
    ull e = 1, ans = 0;
    for (int i = 1; i <= N; i++){
        for (int j = 1; j <= M; j++) {
            if (dis[i][j] == inf) dis[i][j] = 0;
            // printf("%d ", dis[i][j]);
            ans += e * dis[i][j] * dis[i][j];
        }
        // printf("\n");
    }
    printf("%llu\n", ans);
    return 0;
}

H - Sugar Sweet II

Solution

對於一個事件:

  • \(a_i<b_i\) 的話,那麼這個事件時必然時間
  • \(a_i>b_i+w_{b_i}\) 的話,那麼這個時間不可能事件
  • 否則就是可能發生或者肯可能不發生

我們需要計算的就是第三類時間發生的機率

如果我們對 \(b_i\Rightarrow i\) 連邊,此時構成一個基環樹

我們定義 \(L_i\) 表示 \(i\) 節點到距離最近的必然發生的時間的機率

此時 \(p(i)=\frac{1}{L_i !}\)\(p(i)\) 表示 \(i\) 增加的機率

如果在到必然事件的路徑上出現了不可能事件,那麼 \(p(i)=0\)

具體實現時,可以把必然事件都丟到佇列裡面去進行多源 BFS

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN = 5e5+5;
const ll MOD = 1e9 + 7;
ll Tex, n, a[MAXN], b[MAXN], c[MAXN], inv[MAXN];

// init
bool flag[MAXN];
vector<ll> mp[MAXN];
ll p[MAXN];

ll fastPow(ll a, ll b){
	ll res = 1;
	while(b){
		if(b & 1) res = (res * a) % MOD;
		a = (a * a) % MOD;
		b >>= 1;
	}
	return res;
}
void AC(){
	cin >> n;
	for(int i = 1; i <= n; i ++){
		cin >> a[i];
		mp[i].clear();
		flag[i] = 0;
		p[i] = 0;
	}
	for(int j = 1; j <= n; j ++){
		cin >> b[j];
	}
	for(int i = 1; i <= n; i ++){
		cin >> c[i];
	}
	queue<pair<ll,ll>> op;
	for(int i = 1; i <= n; i ++){
		if(a[b[i]] + c[b[i]] > a[i]){
			mp[b[i]].push_back(i);
		}
		if(a[b[i]] > a[i]){
			op.push({i, 1});
			p[i] += 1;
			flag[i] = 1;
		}
	}
	while(!op.empty()){
		pair<ll, ll> qwq = op.front();
		op.pop();
		for(int i = 0; i < mp[qwq.first].size(); i ++){
			if(flag[mp[qwq.first][i]]) continue;
			flag[mp[qwq.first][i]] = 1;
			p[mp[qwq.first][i]] += inv[qwq.second + 1];
			op.push({mp[qwq.first][i], qwq.second + 1});
		}
	}
	for(int i = 1; i <= n; i ++){
		cout << (a[i] + (c[i] * p[i]) % MOD) % MOD << " ";
	}
	cout << endl;
}
int main(){
	ios::sync_with_stdio(false);
	inv[1] = 1;
	for(int i = 2; i <= 500000; i ++){
		inv[i] = (inv[i - 1] * i) % MOD;
	}
	inv[500000] = fastPow(inv[500000], MOD - 2);
	for(int i = 499999; i >= 1; i --){
		inv[i] = (inv[i + 1] * (i + 1)) % MOD;
	}
	cin >> Tex;
	while(Tex --) AC();
	return 0;
}

相關文章