1. 程式人生 > 實用技巧 >2020杭電多校#7(補)

2020杭電多校#7(補)

1007 Game

題意

​ 在二維圖上有n個點,從第一個點開始,然後兩個人依次選點,假設先手選的A且\(|OA|=d\),則後手選B點的時候必須滿足\(|AB|>d\)。直到有一個人不能選擇新的點為止,問先手能不能贏。

思路

​ 暴力將每兩個點之間的距離以及這兩個點存起來,倒敘遍歷距離,距離相等的可以看成一個層次。當第一個點所在的層次不止有一個點時,先手的最優策略為在第一個點的同層次選點,後手只能跨層次,而由於一個層次最少有兩個點,因此先手可以繼續選擇而必勝。

程式碼

#include <bits/stdc++.h>
#define ll long long
#define pll pair<ll, ll>
using namespace std;
const int maxn = 1e5 + 7;
const int mod = 1e9 + 7;

int T, n, vis[maxn];
pll a[maxn];
struct node {
    int a, b;
    ll dis;
};

ll dis(pll a, pll b) {
    return (a.first - b.first) * (a.first - b.first) + (a.second - b.second) * (a.second - b.second);
}

bool cmp(node a, node b) {
    return a.dis > b.dis;
}

int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    freopen("data.in", "r", stdin);
    //freopen("data.out", "w", stdout);
    cin >> T;
    while(T--) {
        cin >> n;
        memset(vis, 0, sizeof(vis));
        for(int i = 0; i < n; i++) {
            cin >> a[i].first >> a[i].second;
        }
        vector<node> vec;
        for(int i = 0; i < n; i++) {
            for(int j = i + 1; j < n; j++) {
                vec.push_back(node{i, j, dis(a[i], a[j])});
            }
        }
        sort(vec.begin(), vec.end(), cmp);
        string ans = "NO";
        for(auto item: vec) {
            if(vis[item.a] || vis[item.b]) continue;
            vis[item.a] = vis[item.b] = 1;
            if(item.a == 0 || item.b == 0) ans = "YES";
        }
        cout << ans << endl;
    }
    return 0;
}

1009 Increasing and Decreasing

題意

​ 問能否構造出一個長度為n,最長上升子序列長度為x,下降子序列長度為y的排列。

思路

​ 可以考慮構造類似321 654 987....這樣的序列下降長度就是最大的塊的長度,上升長度就是塊的數量。考慮極端情況,要滿足題意則有\(n > x*y \quad and \quad n<x+y-1\),然後從後向前依次構造,保證過程中一直\(n,x,y\)一直滿足條件。

程式碼

ll T, n, x, y;
int a[maxn], top;

int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    freopen("data.in", "r", stdin);
    //freopen("data.out", "w", stdout);
    cin >> T;
    while(T--) {
        cin >> n >> x >> y;
        if(n > x * y || n < x + y - 1) {
            cout << "NO\n";
            continue;
        }
        cout << "YES\n";
        int top = n;
        int now = n;
        while(n) {
            if(n >= x + y - 1) {
                for(int i = n - y; i < n; i++) a[i] = now--;
                x--;
                n -= y;
            }
            else {
                while(n < x + y - 1) y--;
            }
        }
        for(int i = 0; i < top; i++) {
            cout << a[i];
            if(i != top - 1) cout << ' ';
            else cout << "\n";
        }
    }
    return 0;
}

1010 Jogging

題意

​ 在無窮平面上給一個點,從這個點出發向任意\((x,y)\)走,要求\(gcd(x,y)>1\),詢問經過\(t\)次走動後能回到起點的期望。

思路

​ 首先知道當能走到\((x,y) \quad, \quad x==y\)時,此時的概率為0,其他的情況則暴力走出所有能到達的點,答案是起點能到達的點為分子,所有點能到達的點總數為分母。

程式碼

ll x, y, up, down;
map<pair<ll, ll>, int> mp;
ll d[8][2] = {{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}};

ll gcd(ll a, ll b) {
    return !b ? a : gcd(b, a % b);
}

int all(ll x, ll y) {
    int res = 1;
    for(int i = 0; i < 8; i++) {
        if(gcd(x + d[i][0], y + d[i][1]) > 1) res++;
    }
    return res;
}

void dfs(ll x, ll y) {
    if(x == y) down = -1;
    if(down == -1) return;

    mp[mkp(x, y)]++;
    down += all(x, y);
    for(int i = 0; i < 8; i++) {
        ll tx = x + d[i][0], ty = y + d[i][1];
        if(!mp.count(mkp(tx, ty)) && gcd(tx, ty) > 1) {
            dfs(tx, ty);
        }
    }
}

void slove() {
    mp.clear();
    scanf("%lld%lld", &x, &y);
    up = all(x, y), down = 0;
    dfs(x, y);
    if(down == -1) {
        printf("0/1\n");
        return;
    }
    ll temp = gcd(up, down);
    up /= temp; down /= temp;
    printf("%lld/%lld\n", up, down);
}