1. 程式人生 > 實用技巧 >Java多執行緒-執行緒死鎖問題

Java多執行緒-執行緒死鎖問題

目錄

Contest Info


傳送門

Solved A B C D E F G H I J K
8 / 11 Ø O Ø O O O - Ø O - -
  • O 在比賽中通過
  • Ø 賽後通過
  • ! 嘗試了但是失敗了
  • - 沒有嘗試

Solutions


A. Portal

題意:
給出一張\(n\)個點,\(m\)條邊的無向圖,現在有\(k\)

個認為,每個任務給定\(u_i,v_i\),意即要先到\(u_i\),然後再到\(v_i\)
現在可以在當前所在位置設定傳送門,兩個傳送門之間可以瞬時傳送,圖上最多存在兩個傳送門。
問從\(1\)號點出發,完成所有任務的最小路徑。

思路:
樸素解法:\(dp[i][j][p]\)表示當前在點\(i\),在做任務\(j\)並且\(p\)位置有個傳送門。那麼可以直接分情況進行轉移。但是複雜度為\(O(n^4)\)
考慮同一個階段的兩個子任務是等價的,所以將他們拆分為兩個任務,所以現在任務就變為了從一個點到另外一個點。這樣的話我們可以減少一維“位置”的狀態,因為起點和終點固定,所以可以直接通過列舉中間點進行轉移。
時間複雜度為\(O(n^3)\)

Code
// Author : heyuhhh
// Created Time : 2020/07/25 15:29:35
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 300 + 5;
 
int n, m, k;
ll dis[N][N];
ll dp[N << 1][N];
 
void chkmin(ll& x, ll y) {
    if (x > y) x = y;
}
 
void run() {
    cin >> n >> m >> k;
    memset(dis, INF, sizeof(dis));
    for (int i = 1; i <= m; i++) {
        int u, v, W;
        cin >> u >> v >> W;
        if ((ll)W <= dis[u][v]) {
            dis[u][v] = dis[v][u] = W;
        }
    }
    for (int i = 1; i <= n; i++) {
        dis[i][i] = 0;
    }
    for (int k = 1; k <= n; k++) {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; j++) {
                dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
            }
        }
    }
    vector<int> vs;
    vs.push_back(1);
    for (int i = 0; i < k; i++) {
        int u, v;
        cin >> u >> v;
        vs.push_back(u);
        vs.push_back(v);
    }
    int sz = sz(vs);
    memset(dp, INF, sizeof(dp));
    ll MAX = ll(0x3f3f3f3f3f3f3f3f);
    dp[0][1] = 0;
    for (int i = 0; i < sz - 1; i++) {
        int u = vs[i], v = vs[i + 1];
        for (int p = 1; p <= n; p++) {
            if (dp[i][p] != MAX) {
                chkmin(dp[i + 1][p], dp[i][p] + dis[u][v]);
                chkmin(dp[i + 1][p], dp[i][p] + dis[p][v]);
                for (int q = 1; q <= n; q++) {
                    chkmin(dp[i + 1][q], min(dp[i][p] + dis[u][q] + dis[p][v], dp[i][p] + min(dis[u][q], dis[p][q]) + dis[q][v]));
                }
            }
        }
    }
    ll ans = MAX;
    for (int i = 1; i <= n; i++) {
        ans = min(ans, dp[sz - 1][i]);
    }
    cout << ans << '\n';
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}

B. Graph

有這樣一個性質,任一時刻,連線兩點\((u,v)\)他們的邊權值都是相等的。
證明:考慮現在新增一條邊有多個環,那麼顯然這條邊的權值只有一個,也就是其餘所有路徑上的邊異或值相等,那麼就容易發現任一狀態這條邊的權值都是相等的。
然後就是異或最小生成樹。可以直接通過字典序自底向上合併即可。每次合併就貪心找權值最小的合併。

Code
// Author : heyuhhh
// Created Time : 2020/07/25 18:53:18
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;

int n;
vector<pii> G[N];
int a[N];

void dfs(int u, int fa, int val) {
    a[u] = val;
    for (auto it : G[u]) {
        int v = it.fi, w = it.se;
        if (v == fa) continue;
        dfs(v, u, val ^ w);
    }
}

int ch[N * 31][2];
int tot;

void insert(int x) {
    int p = 0;
    for (int i = 29; i >= 0; i--) {
        int op = 0;
        if (x >> i & 1) {
            op = 1;
        }
        if (!ch[p][op]) {
            ch[p][op] = ++tot;
        }
        p = ch[p][op];
    }
}

ll ans;

int calc(int p1, int p2, int dep) {
    int res = INF;
    if (ch[p1][0] && ch[p2][0]) {
        res = calc(ch[p1][0], ch[p2][0], dep - 1);
    }
    if (ch[p1][1] && ch[p2][1]) {
        res = min(res, calc(ch[p1][1], ch[p2][1], dep - 1));
    }
    if (res != INF) return res;
    if (ch[p1][0] && ch[p2][1]) {
        res = min(res, calc(ch[p1][0], ch[p2][1], dep - 1) + (1 << dep));
    }
    if (ch[p1][1] && ch[p2][0]) {
        res = min(res, calc(ch[p1][1], ch[p2][0], dep - 1) + (1 << dep));
    }
    if (res == INF) res = 0;
    return res;
}

void work(int p, int dep) {
    if (ch[p][0]) {
        work(ch[p][0], dep - 1);
    }
    if (ch[p][1]) {
        work(ch[p][1], dep - 1);
    }
    if (ch[p][0] && ch[p][1]) {
        ans += (1 << dep);
        ans += calc(ch[p][0], ch[p][1], dep - 1);
    }
}

void run() {
    cin >> n;
    for (int i = 1; i < n; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        ++u, ++v;
        G[u].push_back(MP(v, w));
        G[v].push_back(MP(u, w));
    }
    dfs(1, 0, 0);
    for (int i = 1; i <= n; i++) {
        insert(a[i]);
    }
    work(0, 29);
    cout << ans << '\n';
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}

C. Easy

題意:
求兩個正整數序列\(A,B\),滿足\(\sum_{i=1}^ka_i=n,\sum_{i=1}^kb_i=m\)時,\(\sum\prod_{i=1}^{k}min(a_i,b_i)\)的值。
\(n,m\leq 10^6\)

思路:
愉快的推公式題,考慮二元生成函式,我們首先來求\(\displaystyle f(x,y)=\sum_{i=1}^{\infin}\sum_{j=1}^{\infin}min(i,j)x^iy^j\)的值,那麼\([x^ny^m]f(x)^k\)即是答案。
這種我們肯定首先考慮將min拆開,所以有:

\[\begin{aligned} f(x,y)=&\sum_{i=1}^{\infin}ix^iy^i+\sum_{i=1}^{\infin}\sum_{j=i+1}^{\infin}ix^iy^j+\sum_{j=1}^{\infin}\sum_{i=j+1}^{\infin}jx^iy^j\\ =&\sum_{i=1}^{\infin}i(xy)^i[1+y+y^2+\cdots+x+x^2+\cdots]\\ =&(\frac{y}{1-y}+\frac{x}{1-x}+1)\cdot \frac{xy}{(1-xy)^2}\\ =&\frac{xy}{(1-x)(1-y)(1-xy)} \end{aligned} \]

這個推出來那麼後面就比較簡單了,因為要求\(f(x,y)^k\),我們首先看\(\displaystyle\frac{xy}{1-xy}\),這個就等於\(\displaystyle \sum_{i=1}^{\infin}x^iy^i\),很容易求出對其\(k\)次方過後對應項的係數,根據二項式定理和組合數算一下就行,剩下的不夠\(n,m\)的直接從\(\frac{1}{1-x},\frac{1}{1-y}\)裡面補就行。

Code
// Author : heyuhhh
// Created Time : 2020/07/25 21:05:25
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e6 + 5, MOD = 998244353;
int qpow(ll a, ll b) {
    ll res = 1;
    while(b) {
        if(b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;   
    }
    return res;   
}
int fac[N], inv[N];
void init() {
    fac[0] = 1;
    for(int i = 1; i < N; i++) fac[i] = 1ll * fac[i - 1] * i % MOD;
    inv[N - 1] = qpow(fac[N - 1], MOD - 2);
    for(int i = N - 2; i >= 0; i--) inv[i] = 1ll * inv[i + 1] * (i + 1) % MOD;
}

int C(int n, int m) {
    return 1ll * fac[n] * inv[m] % MOD * inv[n - m] % MOD;
}

int n, m, k;

int calc(int x) {
    return C(x + k - 1, k - 1);
}

void run() {
    cin >> n >> m >> k;
    int ans = 0;
    for (int i = 0; i + k <= min(n, m); i++) {
        ans = (ans + 1ll * C(k - 1 + i, k - 1) * calc(n - (i + k)) % MOD * calc(m - (i + k)) % MOD) % MOD;
    }
    cout << ans << '\n';
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    init();
    int T; cin >> T; while(T--)
    run();
    return 0;
}

D. Drop Voicing

操作就等價於將一個數插入在任意一個位置。
那麼直接列舉所有可能迴圈,求lis即可。

Code
#include<bits/stdc++.h>
 
using namespace std;
 
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define LC k<<1
#define RC k<<1|1
 
typedef long long LL;
const int N=510;
const int M=1100000;
const LL mod=1e9+7;
 
int n;
int a[N],b[N], f[N];
int ans;
int main()
{
    scanf("%d",&n);
    for (int i=0;i<n;i++)
        scanf("%d",&a[i]),a[i]--;
    ans=n;
    for (int i=0;i<n;i++)
    {
        int tmp=1;
        for (int j=0;j<n;j++)
            b[(i+j)%n]=a[j];
        for (int j = 0; j < n; j++) {
            f[j] = 1;
        }
        for (int j = 1; j < n; j++) {
            for (int k = 0; k < j; k++) {
                if (b[j] > b[k])
                f[j] = max(f[j], f[k] + 1);
            }
            tmp = max(tmp, f[j]);
        }
        ans=min(ans,n - tmp);
    }
    printf("%d\n",ans);
    return 0;
}

E. Bogo Sort

對所有環長求lcm即是答案,不用取模。

Code
n = int(input())
a = input().strip().split(" ")
pp = [0 for _ in range(n)]
 
for i in range(n):
    a[i] = int(a[i]) - 1
 
ans = 1
pw = 1
for i in range(n):
    pw = pw * 10
flag = False
for i in range(n):
    if pp[i] == 0:
        now = i
        len = 0
        mx = 0
        num = 0
        last = -1
        tmp = []
        while pp[now] == 0:
            tmp.append(now)
            pp[now] = 1
            if now > last:
                last = now
            else:
                last = now
                num += 1
            if num == 1:
                if tmp[-1] > tmp[0]:
                    flag= True
            elif num > 1:
                flag = True
            now = a[now]
            len += 1
        if flag:
            ans = 0
            break
        ans = ans * len
print(ans % pw)

F. DPS

簽到。

Code

H. Interval

題意:
給定一個序列\(A\),然後會有若干組詢問,每組詢問一段區間\([l,r]\),回答區間中連續子區間中的數的交一共有多少種。強制線上。

思路:
有一個比較重要的性質:

  • 固定一個區間的左端點,那麼不同的數一共只會有\(O(logA)\)種。

所以一共只會有\(O(nlogA)\)種數值,我們把每種數值的區間扣出來,那麼對於一個區間查詢而言實際上就是一個普通二位偏序,可以通過主席樹解決。
問題在於如何去重,比如一個區間覆蓋了\([l_1,r_1],[l_2,r_2],[l_3,r_3]\)這三段,但實際上貢獻只會產生\(1\)。這裡實際上是個小技巧,我們只需要新增加貢獻為\(-1\)\([l_1,r_2],[l_2,r_3]\)即可。
時間複雜度和空間複雜度都是\(O(nlog^2n)\)的。
細節見程式碼:

Code
// Author : heyuhhh
// Created Time : 2020/07/26 11:22:01
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;

int n;
int f[N][20], lg[N];

void init() {
    lg[2] = 1;
    for (int i = 3; i < N; i++) {
        lg[i] = lg[i >> 1] + 1;
    }
    for (int j = 1; j < 20; j++) {
        for (int i = 1; i <= n; i++) {
            if (i + (1 << (j - 1)) <= n) {
                f[i][j] = (f[i][j - 1] & f[i + (1 << (j - 1))][j - 1]);
            }
        }
    }
}

int query(int l, int r) {
    int k = lg[r - l + 1];
    return (f[l][k] & f[r - (1 << k) + 1][k]);
}

map<int, vector<pii>> mp;
vector<pii> v[N];

int rt[N], tot;
int lc[N * 30 * 23], rc[N * 30 * 23], sumv[N * 30 * 23];

void build(int &o, int l, int r) {
    o = ++tot;
    if(l == r) return;
    int mid = (l + r) >> 1;
    build(lc[o], l, mid); build(rc[o], mid + 1, r);
}

void insert(int& o, int last, int l, int r, int p, int v) {
    o = ++tot;
    lc[o] = lc[last], rc[o] = rc[last];
    sumv[o] = sumv[last] + v;
    if (l == r) return;
    int mid = (l + r) >> 1;
    if (p <= mid) insert(lc[o], lc[last], l, mid, p, v);
    else insert(rc[o], rc[last], mid + 1, r, p, v);
}

int query(int o, int l, int r, int L, int R) {
    if (!o) return 0;
    if (L <= l && r <= R) {
        return sumv[o];
    }
    int res = 0;
    int mid = (l + r) >> 1;
    if (L <= mid) {
        res += query(lc[o], l, mid, L, R);
    }
    if (R > mid) {
        res += query(rc[o], mid + 1, r, L, R);
    }
    return res;
}

void run() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> f[i][0];
    }
    init();
    for (int i = 1; i <= n; i++) {
        for (int j = i; j <= n; j++) {
            int l = j + 1, r = n + 1, mid;
            int k = query(i, j);
            while (l < r) {
                mid = (l + r) >> 1;
                if (query(i, mid) == k) l = mid + 1;
                else r = mid;
            }
            mp[k].push_back(MP(i, j));
            // cout << k << ' ' << i << ' ' << l - 1 << '\n';
            j = l - 1;
        }
    }
    for (auto it : mp) {
        sort(all(it.se), [&](pii A, pii B) {
            if (A.fi != B.fi) return A.fi < B.fi;
            return A.se < B.se;
        });
        vector<pii>& now = it.se;
        for (int i = 0; i < sz(now); i++) {
            v[now[i].se].push_back(MP(now[i].fi, 1));
            if (i) {
                v[now[i].se].push_back(MP(now[i - 1].fi, -1));
            }
        }
    }
    build(rt[0], 1, n);
    for (int i = 1; i <= n; i++) {
        sort(all(v[i]));
        rt[i] = rt[i - 1];
        for (auto it : v[i]) {
            insert(rt[i], rt[i], 1, n, it.fi, it.se);
        }
    }
    int q; cin >> q;
    int lastans = 0;
    while (q--) {
        int l, r;
        cin >> l >> r;
        l = (l ^ lastans) % n + 1;
        r = (r ^ lastans) % n + 1;
        if (l > r) swap(l, r);
        int ans = query(rt[r], 1, n, l, r);
        cout << ans << '\n';
        lastans = ans;
    }
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}

I. Hard Math Problem

答案就為\(\frac{2}{3}\)
主要思路就是一個E周圍貢獻四個H,G用來匹配已經產生了貢獻的H,那麼相當於一個G對應零個H,所以總的比例就為\(\frac{2}{3}\)

Code
// Author : heyuhhh
// Created Time : 2020/07/25 12:31:21
#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
#define pb push_back
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run() {
    cout << "0.666667" << '\n';
}
int main() {
#ifdef Local
    freopen("input.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0); cout.tie(0);
    cout << fixed << setprecision(20);
    run();
    return 0;
}