zoukankan      html  css  js  c++  java
  • The Preliminary Contest for ICPC Asia Xuzhou 2019

    传送门

    A. Who is better?

    扩展中国剩余定理+斐波那契博弈,没啥好说的,关于斐波那契博弈,详见:传送门

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 15;
    const ll MAX = 1e15;
    int k;
    ll a[N], b[N];
    
    void exgcd(ll a, ll b, ll &x, ll &y) {
        if(b == 0) {
            x = 1, y = 0;
            return ;
        }
        exgcd(b, a % b, x, y);
        int t = x;
        x = y;
        y = t - (a / b) * y;
    }
    
    ll fib[100];
    map <ll, bool> mp;
    void pre() {
        fib[1] = fib[2] = 1;
        for(int i = 3; ;i++) {
            fib[i] = fib[i - 2] + fib[i - 1];
            if(fib[i] > MAX) break;
            mp[fib[i]] = 1;
        }
        mp[1] = 1;
    }
    
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        pre();
        cin >> k;
        for(int i = 1; i <= k; i++) {
            cin >> a[i] >> b[i];
        }
        ll n = b[1], m = a[1];
        for(int i = 2; i <= k; i++) {
            ll g = __gcd(m, a[i]);
            ll tmp = ((b[i] - n) % a[i] + a[i]) % a[i];
            if(tmp % g != 0) {
                cout << "Tankernb!"; return 0;
            }
            ll x, y;
            exgcd(m / g, a[i] / g, x, y);
            x = x * (tmp / g);
            x = (x % a[i] + a[i]) % a[i];
            n += x * m;
            m = m / g * a[i];
            n %= m;
            if(n > MAX) {
                cout << "Tankernb!"; return 0;
            }
        }
        if(mp[n]) cout << "Lbnb!";
        else cout << "Zgxnb!";
        return 0;
    }
    
    

    B. so easy

    并查集+(map)就行,还可以离散化搞一下,把(i+1)顺便离散化一下就行。

    Code
    #include<bits/stdc++.h>
    typedef long long ll;
    typedef unsigned long long ull;
    typedef double db;
    const int MAXN = 1e6+5,MAXM = 1e6+5,MOD = 1e9+7,INF = 0x3f3f3f3f,N=100050;
    const ll INFL = 0x3f3f3f3f3f3f3f3f;
    const db eps = 1e-9;
    #define lson o<<1,l,m
    #define rson o<<1|1,m+1,r
    #define mid l + ((r-l)>>1)
    #define rep(i,a,b) for(register int i=(a);i<=(b);i++)
    #define vii vector<pair<int,int>>
    #define vi vector<int>
    using namespace std;
    int n,q,X[MAXN<<1],len,fa[MAXN<<1],v[MAXN],v2[MAXN],mp[MAXN<<1];
    struct Ques{
        int z,x;
    }Q[MAXM];
    int find(int x){
        return x==fa[x]?x:fa[x]=find(fa[x]);
    }
    struct Istream {
        template <class T>
        Istream &operator >>(T &x) {
            static char ch;static bool neg;
            for(ch=neg=0;ch<'0' || '9'<ch;neg|=ch=='-',ch=getchar());
            for(x=0;'0'<=ch && ch<='9';(x*=10)+=ch-'0',ch=getchar());
            x=neg?-x:x;
            return *this;
        }
    }fin;
    int main(){
        //ios::sync_with_stdio(false);cin.tie(0);
        //freopen("../A.in","r",stdin);
        //freopen("../A.out","w",stdout);
        fin>>n>>q;
        for(register int i=1;i<=q;i++){
            fin>>Q[i].z>>Q[i].x;
            X[++len]=Q[i].x;
            X[++len]=Q[i].x+1;
        }
        sort(X+1,X+1+len);
        len=unique(X+1,X+1+len)-X-1;
        for(int i=1;i<=len;i++)fa[i]=i;
        for(int i=1;i<=q;i++){
            v[i] = lower_bound(X+1,X+1+len,Q[i].x)-X;
            v2[i] = lower_bound(X+1,X+1+len,Q[i].x+1)-X;
            mp[v[i]] = Q[i].x;
            mp[v2[i]] = Q[i].x+1;
        }
        int ans=0;
        for(int i=1;i<=q;i++){
            if(Q[i].z==1){
                if(fa[v[i]]==v[i])fa[v[i]] = find(v2[i]);
            }else{
                ans=mp[find(v[i])];
                if(ans==n+1)printf("-1
    ");
                else printf("%d
    ",ans);
            }
        }
        return 0;
    }
    

    C. Buy Watermelon

    不知道啥题,队友说有点坑= =

    Code
    #include<bits/stdc++.h>
    using namespace std;
    int main() {
        int w; cin>>w;
        if(w>=4 && w%2==0) puts("YES");
        else puts("NO");
    }
    

    D. Carneginon

    KMP模板题。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e5 + 5;
    
    int q;
    int nxtT[N], nxtS[N], nxt[N];
    
    char T[N], S[N];
    
    void Get_next(char *s, int *nxt) {
        int j, L = strlen(s + 1);
        nxt[1] = j = 0;
        for(int i = 2; i <= L; i++) {
            while(j && s[i] != s[j + 1]) j = nxt[j];
            if(s[i] == s[j + 1]) j++;
            nxt[i] = j;
        }
    }
    
    bool cmp(char *s1, char *s2, int op) {
        if(op) memcpy(nxt, nxtS, sizeof(nxtS));
        else memcpy(nxt, nxtT, sizeof(nxtT));
        int L1 = strlen(s1 + 1), L2 = strlen(s2 + 1);
        for(int i = 1, j = 0; i <= L1; i++) {
            while(j > 0 && (j == L2 || s1[i] != s2[j + 1])) j = nxt[j];
            if(s1[i] == s2[j + 1]) j++;
            if(j == L2) return true;
        }
        return false;
    }
    
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> T + 1 >> q;
        Get_next(T, nxtT);
        int lenT = strlen(T + 1);
        while(q--) {
            cin >> S + 1;
            int lenS = strlen(S + 1);
            Get_next(S, nxtS);
            if(lenS == lenT) {
                if(cmp(S, T, 1)) cout << "jntm!" << '
    ';
                else cout << "friend!" << '
    ';
            } else if(lenS < lenT) {
                if(cmp(T, S, 1)) cout << "my child!" << '
    ';
                else cout << "oh, child!" << '
    ';
            } else {
                if(cmp(S, T, 0)) cout << "my teacher!" << '
    ';
                else cout << "senior!" << '
    ';
            }
        }
        return 0;
    }
    
    

    E. XKC's basketball team

    题意:
    蔡徐坤的篮球队...
    给出(n)个数,每个数为(w_i),现在对于第(i)个位置,找最远的一个(j),满足(w_jgeq w_i+m)

    思路:
    注意到这个(j)的选取具有单调性就行了,然后维护后缀最大值二分一下即可。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 5e5 + 5;
    
    int n, m;
    int w[N], mx[N];
    
    bool chk(int x, int i) {
        return mx[x] >= w[i] + m;
    }
    
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> n >> m;
        for(int i = 1; i <= n; i++) cin >> w[i];
        for(int i = n; i; i--) mx[i] = max(w[i], mx[i + 1]);
        for(int i = 1; i <= n; i++) {
            int l = i + 1, r = n + 1, mid;
            while(l < r) {
                mid = (l + r) >> 1;
                if(chk(mid, i)) l = mid + 1;
                else r = mid;
            }
            cout << l - 2 - i;
            if(i != n) cout << ' ';
        }
        return 0;
    }
    
    

    F. Little M's attack plan

    题意:
    给出一颗(n)个结点的树,每个结点有权值(p_i)
    之后回答(q)个询问,每个询问给出(v_i,k_i),回答距离点(v_i)距离不大于(k_i)的所有点的权值和。
    其中(qleq 5000,0leq k_ileq 100)

    思路:

    • 思考这样一个问题,假设询问只包含子树结点的时候怎么做?
    • 这样的做法有很多,可以主席树维护深度,在进入子树的时候统计一下答案,出子树的时候统计一下答案,两者相减即可。
    • 但为啥要用主席树,这种事不是权值线段树或者权值树状数组就行了么...
    • 现在回到原问题,注意(q,k)都比较小,两者相乘也才(5e5),所以想到将问题拆分!
    • 假设现在的询问为((v,k)),当前在(v)子树中深度相差不超过(k)的询问的答案为(F(v,k))。那么我们求出了(F(v,k)),还需要求(F(fa[v],k-1)),之后有一部分重合,我们就减去(F(v,k-2))...然后依次这样算就行了。

    总的来说,就是利用容斥+拆分询问的思想,有时候一个询问比较复杂,可以考虑将询问拆分成多个,每次只回答一些子问题就行了。这跟多维偏序有点像。。
    注意一下细节,比如(k=0)的情况什么的,以及跳到根就没必要再跳了。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1000005;
    
    template <class T>
    inline void read(T& x) {
        static char c;
        x = 0;
        bool sign = 0;
        while (!isdigit(c = getchar()))
            if (c == '-')
                sign = 1;
        for (; isdigit(c); x = x * 10 + c - '0', c = getchar())
            ;
        if (sign)
            x = -x;
    }
    
    int n;
    ll a[N];
    
    struct Edge{
        int v, next;
    }e[N << 1];
    int head[N], tot;
    void adde(int u, int v) {
        e[tot].v = v; e[tot].next = head[u]; head[u] = tot++;
    }
    
    int fa[N], d[N];
    void getfa(int u, int Fa) {
        fa[u] = Fa; d[u] = d[Fa] + 1;
        for(int i = head[u]; i != -1; i = e[i].next) {
            int v = e[i].v;
            if(v != Fa) getfa(v, u);
        }
    }
    
    struct Query{
        int k, op, id;
    };
    vector <Query> q[N];
    
    ll c[N];
    int lowbit(int x) {return x & (-x);}
    
    void add(int x, ll v) {
        for(; x < N; x += lowbit(x)) c[x] += v;
    }
    
    ll sum(int x) {
        ll ans = 0;
        for(; x; x -= lowbit(x)) ans += c[x];
        return ans;
    }
    ll sum(int l, int r) {
        return sum(r) - sum(l - 1);
    }
    
    ll res[N];
    
    void dfs(int u, int fa) {
        for(auto it : q[u]) {
            int k = it.k, id = it.id, op = it.op;
            res[id] -= op * sum(d[u], d[u] + k);
        }
        add(d[u], a[u]);
        for(int i = head[u]; i != -1; i = e[i].next) {
            int v = e[i].v;
            if(v != fa) dfs(v, u);
        }
        for(auto it : q[u]) {
            int k = it.k, id = it.id, op = it.op;
            res[id] += op * sum(d[u], d[u] + k);
        }
    }
    
    int main() {
    //    freopen("input.in", "r", stdin);
        read(n);
        for(int i = 1; i <= n; i++) read(a[i]);
        memset(head, -1, sizeof(head));
        for(int i = 1; i < n; i++) {
            int u, v; read(u), read(v);
            adde(u, v); adde(v, u);
        }
        getfa(1, 0);
        int t; read(t);
        for(int i = 1; i <= t; i++) {
            int v, k; read(v), read(k);
            q[v].push_back({k, 1, i});
            while(fa[v] && (--k) >= 0) {
                q[fa[v]].push_back({k, 1, i});
                if(k - 1 >= 0) q[v].push_back({k - 1, -1, i});
                v = fa[v];
            }
        }
    //    for(int i = 1; i <= tot; i++) {
    //        cout << q[i].v << ' ' << q[i].k << ' ' << q[i].op << '
    ';
    //    }
        dfs(1, 0);
        for(int i = 1; i <= t; i++) {
            printf("%lld
    ", res[i]);
        }
        return 0;
    }
    

    G. Colorful String

    在回文自动机上面(dfs)一下,统计一下即可。利用一个桶来维护一下当前出现的次数,注意还要乘上回文串出现的次数。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 3e5 + 5;
    
    int n;
    char s[N];
    
    namespace PAM{
        int ch[N][26], fail[N], len[N], st[N], cnt[26], num[N];
        int sz, n, last;
        ll ans, cur;
        int New(int l, int f) {
            memset(ch[++sz], 0, sizeof(ch[sz]));
            len[sz] = l, fail[sz] = f;
            return sz;
        }
        void init() {
            sz = -1; ans = cur = 0;
            New(0, 1); last = New(-1, 0);
            st[n = 0] = -1;
            memset(cnt, 0, sizeof(cnt));
            memset(num, 0, sizeof(num));
        }
        int getf(int x) {
            while(st[n - len[x] - 1] != st[n]) x = fail[x];
            return x;
        }
        bool Insert(int c) { //int
            st[++n] = c;
            int x = getf(last);
            bool F = 0;
            if(!ch[x][c]) {
                F = 1;
                int f = getf(fail[x]);
                ch[x][c] = New(len[x] + 2, ch[f][c]);
            }
            last = ch[x][c];
            ++num[last];
            return F;
        }
        void count() {
            for(int i = sz; i >= 2; i--) num[fail[i]] += num[i];
        }
        void debug() {
            cout << sz << '
    ';
            for(int i = 1; i <= sz; i++) cout << num[i] << ' ';
            cout << '
    ';
        }
        void dfs(int u) {
            for(int i = 0; i < 26; i++) {
                int v = ch[u][i];
                if(v) {
                    if(++cnt[i] == 1) ++cur;
                    ans += cur * num[v];
                    dfs(v);
                    if(--cnt[i] == 0) --cur;
                }
            }
        }
    };
    
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> s + 1;
        n = strlen(s + 1);
        PAM::init();
        for(int i = 1; i <= n; i++) {
            PAM::Insert(s[i] - 'a');
        }
    
        PAM::count();
        PAM::dfs(0);
        PAM::dfs(1);
        cout << PAM::ans;
        return 0;
    }
    

    H. function

    参见:传送门

    I. query

    题意:
    给出一个(n)的排列,现在回答多个询问,对于每个询问([l,r]),回答有多少对((i,j)),满足(lleq i < jleq r)(min(p_i, p_j)=gcd(p_i,p_j))

    思路:
    注意到(min(p_i, p_j)=gcd(p_i,p_j))其实就是(a_i,a_j)为倍数关系,因为(1)(n)的排列中这样的倍数对数量级为(O(nlogn))的,所以我们可以提前找出来所有的对数。
    然后将询问离线,就相当于处理一个简单的二位偏序问题了。
    有很多种做法,余独爱树状数组。

    Code
    #include <bits/stdc++.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define INF 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    const int N = 1e5 + 5;
    
    int n, m;
    int a[N], p[N];
    
    vector <int> v[N];
    vector <pii> Q[N];
    
    int ans[N];
    
    int c[N];
    
    int lowbit(int x) {return x & (-x);}
    
    void add(int x, int v) {
        for(; x < N; x += lowbit(x)) c[x] += v;
    }
    
    int query(int x) {
        int ans = 0;
        for(; x; x -= lowbit(x)) ans += c[x];
        return ans;
    }
    
    int query(int l, int r) {
        return query(r) - query(l - 1);
    }
    
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> n >> m;
        for(int i = 1; i <= n; i++) {
            cin >> a[i]; p[a[i]] = i;
        }
        for(int i = 1; i <= n; i++) {
            for(int j = 2 * i; j <= n; j += i) {
                int x = p[i], y = p[j];
                if(x > y) swap(x, y);
                v[y].push_back(x);
            }
        }
        for(int i = 1; i <= m; i++) {
            int l, r; cin >> l >> r;
            Q[r].push_back(MP(l, i));
        }
        for(int i = 1; i <= n; i++) {
            for(auto it : v[i]) add(it, 1);
            for(auto it : Q[i]) {
                int L = it.fi, id = it.se, R = i;
                ans[id] = query(L, R);
            }
        }
        for(int i = 1; i <= m; i++) cout << ans[i] << '
    ';
        return 0;
    }
    
    

    J. Random Access Iterator

    题意:
    现在有一个(vector),现在每次访问的时候会随机访问一个元素。
    然后现在用这个(vector)去找子树最大深度,每到一个结点时,先找出(size),然后循环(size)次去找儿子(dfs)下去。
    最后问得到的最大深度与实际真正的最大深度相等的概率为多少。

    思路:
    这个题我没想出来,dp这方面太弱了QAQ。
    首先注意这一点:

    • 你成功一次就说明你成功了,要想失败,就得全部失败。

    这不是什么励志鸡汤,这一点可以告诉我们将问题转化一下,求出失败的概率,那么减一下就有成功的概率了。
    考虑树形dp,设(dp[u])表示从(u)出发,能成功到达最大深度的概率。初始化,对于一些深度为最大深度的叶子结点,它们的值为(1),其余全是(0)
    我们考虑对于每个结点,我们算它们失败一次的概率。那么就有:(p=1-frac{1}{size}sum_{vin son}dp[v])。这个还是比较好算的。
    那么既然不能成功,就说明每次失败,那么全部失败的概率就为(p^{size})。所以(dp[u]=1-p^{size})
    转化一下问题过后,就变得很简单了,如果硬要死磕成功,那么就还有第几次成功的问题,或者成功过后再成功什么乱七八糟的,就很难搞了。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e6 + 5, MOD = 1e9 + 7;
    
    ll qp(ll a, ll b) {
        ll ans = 1;
        while(b) {
            if(b & 1) ans = ans * a % MOD;
            a = a * a % MOD;
            b >>= 1;
        }
        return ans;
    }
    
    int n;
    vector <int> g[N];
    
    int sz[N], dep[N];
    int Max;
    
    void dfs(int u, int fa) {
        dep[u] = dep[fa] + 1;
        for(auto v : g[u]) {
            if(v == fa) continue;
            dfs(v, u);
            ++sz[u];
        }
    }
    
    int dp[N];
    
    int add(int x, int y) {
        x += y;
        if(x >= MOD) x -= MOD;
        return x;
    }
    
    int mul(ll a, ll b) {
        a *= b;
        return a % MOD;
    }
    
    void dfs2(int u, int fa) {
        if(dep[u] == Max) {
            dp[u] = 1; return ;
        }
        int p = 0;
        for(auto v : g[u]) {
            if(v == fa) continue;
            dfs2(v, u);
            p = add(p, dp[v]);
        }
        p = mul(p, qp(sz[u], MOD - 2));
        p = 1 - p + MOD;
        int k = qp(p, sz[u]);
        dp[u] = (1 - k + MOD) % MOD;
    }
    
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> n;
        for(int i = 1; i < n; i++) {
            int u, v; cin >> u >> v;
            g[u].push_back(v);
            g[v].push_back(u);
        }
        dfs(1, 0);
        Max = *max_element(dep + 1, dep + n + 1);
        dfs2(1, 0);
        cout << dp[1];
        return 0;
    }
    
    

    K. Center

    题意:
    给出(n)个点,(nleq 1000),现在要在二维平面上找一个点((x_0,y_0)),使得对于每个点((x_i,y_i)),都存在一个点((x_j,y_j))关于((x_0,y_0))中心对称。
    如果不存在一个点((x_j,y_j)),那么就称((x_i,y_i))为孤儿点。
    现在问怎么选择((x_0,y_0)),使得孤儿点最少。

    思路:

    • 注意到我们选择的这个点((x_0,y_0))肯定位于某些个点的中线上,并且这些点的个数越多越好。
    • 注意到点的个数很少,所以我们直接暴力枚举两个点,统计中点次数,然后更新答案就好了。

    注意到如果这个点为某个((x_i,y_i)),那么还需要特判一下再更新答案。
    详见代码:

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1005;
    
    map <int, map<int, int>> mp, cnt;
    
    struct node{
        int x, y;
    }a[N];
    
    int n;
    
    bool chk(int x, int y) {
        return x % 2 == 0 && y % 2 == 0 && mp[x / 2][y / 2] == 1;
    }
    
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> n;
        for(int i = 1; i <= n; i++) {
            cin >> a[i].x >> a[i].y;
            mp[a[i].x][a[i].y] = 1;
        }
        int ansx, ansy;
        int ans = 0;
        for(int i = 1; i <= n; i++) {
            for(int j = i; j <= n; j++) {
                int x = a[i].x + a[j].x, y = a[i].y + a[j].y;
                cnt[x][y]++;
                if(cnt[x][y] > ans) {
                    ans = cnt[x][y];
                    ansx = x, ansy = y;
                } else if(cnt[x][y] == ans && !chk(x, y)) {
                    ans = cnt[x][y];
                    ansx = x, ansy = y;
                }
            }
        }
        ans = n - 2 * cnt[ansx][ansy];
        if(chk(ansx, ansy)) ++ans;
        cout << ans;
        return 0;
    }
    
    

    M. Longest subsequence

    序列自动机预处理下一位,然后在每一位都有两种选择,等于或大于,跟着(t)串跑一遍就行了。
    注意一下细节,注意字典序不能相等,只有严格大于。
    详见代码吧:

    Code
    #include <bits/stdc++.h>
    #define INF 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    const int N = 1e6 + 5;
    
    int nxt[N][26], last[26];
    
    char s[N], t[N];
    
    int n, m;
    
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> n >> m;
        cin >> s + 1 >> t + 1;
        for(int i = n; i >= 0; i--) {
            int p = s[i] - 'a';
            for(int j = 0; j < 26; j++) nxt[i][j] = last[j];
            last[p] = i;
        }
        int i = 0;
        int ans = -1;
        for(int j = 1; j <= m; j++) {
            int tmp = N, p = t[j] - 'a';
            for(int k = p + 1; k < 26; k++) if(nxt[i][k]) tmp = min(tmp, nxt[i][k]);
            if(tmp < N) ans = max(ans, n - tmp + j);
            i = nxt[i][p];
            if(!i) break;
            if(j == m && i < n) {
                ans = max(ans, m + n - i);
            }
        }
        cout << ans;
        return 0;
    }
    
    
  • 相关阅读:
    Python之路Day12--mysql介绍及操作
    Python之路第一课Day11--随堂笔记(异步IO数据库队列缓存之二)
    Python之路第一课Day10--随堂笔记(异步IO数据库队列缓存)
    Python之路第一课Day9--随堂笔记之二(进程、线程、协程篇)
    Python之路第一课Day9--随堂笔记之一(堡垒机实例以及数据库操作)未完待续....
    Python之路第一课Day8--随堂笔记(socket 承接上节---网络编程)
    Python之路第一课Day7--随堂笔记(面向对象编程进阶...未完待续 )
    Python之路第一课Day6--随堂笔记(面向对象 )
    Python之路第一课Day5--随堂笔记(模块)
    Python之路第一课Day4--随堂笔记(迭代生成装饰器)
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/11517259.html
Copyright © 2011-2022 走看看