zoukankan      html  css  js  c++  java
  • (4.24~4.30)

    FFT

    HDU 5452 Minimum Cut

    [POJ 3417] Network这道题差不多。

    LCA + 树上差分。 统计每个节点和它父亲相连的边被覆盖的次数,那切割了这条边形成最小割,还需要切割被覆盖次数条副边。

    求最小值即可。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    const int maxn = 2e5+50;
    typedef long long ll;
    ///加边
    int cnt, h[maxn];
    struct edge
    {
        int to, pre, v;
    } e[maxn << 1];
    void init()
    {
        cnt = 0;
        memset(h, 0, sizeof(h));
    }
    void add(int from, int to, int v)
    {
        cnt++;
        e[cnt].pre = h[from]; ///5-->3-->1-->0
        e[cnt].to = to;
        e[cnt].v = v;
        h[from] = cnt;
    }
    ///LCA
    int dist[maxn];
    int dep[maxn];
    int anc[maxn][33]; ///2分的父亲节点
    void dfs(int u, int fa)
    {
        for(int i = h[u]; i; i = e[i].pre)
        {
            int v = e[i].to;
            if(v == fa) continue;
            dist[v] = dist[u] + e[i].v;
            dep[v] = dep[u] + 1;
            anc[v][0] = u;
            dfs(v, u);
        }
    }
    void LCA_init(int n)
    {
        for(int j = 1; (1 << j) < n; j++)
            for(int i = 1; i <= n; i++) if(anc[i][j-1])
            anc[i][j] = anc[anc[i][j-1]][j-1];
    }
    int LCA(int u, int v)
    {
        int log;
        if(dep[u] < dep[v]) swap(u, v);
        for(log = 0; (1 << log) < dep[u]; log++);
        for(int i = log; i >= 0; i--)
            if(dep[u] - (1 << i) >= dep[v]) u = anc[u][i];
        if(u == v) return u;
        for(int i = log; i >= 0; i--)
            if(anc[u][i] && anc[u][i] != anc[v][i])
                u = anc[u][i], v = anc[v][i];
        return anc[u][0];
    }
    int w[maxn];
    int sum[maxn];
    int dfs2(int u, int fa)
    {
        sum[u] += w[u];
        for(int i = h[u]; i; i = e[i].pre)
        {
            int v = e[i].to;
            if(v == fa) continue;
            sum[u] += dfs2(v, u);
        }
        return sum[u];
    }
    int num[maxn];
    int main()
    {
        int T, kase = 0; scanf("%d", &T);
        while(T--)
        {
            init();
            int n, m; scanf("%d %d", &n, &m);
            for(int i = 1; i < n; i++)
            {
                int u, v; scanf("%d %d", &u, &v);
                add(u, v, 1), add(v, u, 1);
            }
            dfs(1, -1);
            LCA_init(n);
            memset(w, 0, sizeof(w));
            for(int i = 1; i <= m - n + 1; i++)
            {
                int u, v; scanf("%d %d", &u ,&v);
                w[u] += 1, w[v] += 1;
               // printf("%d
    ", LCA(u, v));
                w[LCA(u, v)] += -2;
            }
            memset(sum, 0, sizeof(sum));
            dfs2(1, -1);
            int ans = 1e9 + 7;
            for(int i = 2; i <= n; i++)
            {
                num[i] = sum[i] + 1;
                ans = min(ans, num[i]);
            }
            printf("Case #%d: %d
    ",++kase, ans);
        }
        return 0;
    }
    Code

    明天开始强化数据结构啦。

     食物链

    我记得之前的写法都是分成三个域来做,这次用边带权并查集。

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    using namespace std;
    const int maxn = 5e4 + 50;
    int pre[maxn];
    int d[maxn];
    int Find(int x)
    {
        if(x == pre[x]) return x;
        int root = Find(pre[x]);
        d[x] += d[pre[x]];
        return pre[x] = root;
    }
    int main()
    {
        int n, k; scanf("%d %d", &n, &k);
        for(int i = 1; i <= n; i++) pre[i] = i, d[i] = 0;
        int ans = 0;
        for(int i = 1; i <= k; i++)
        {
            int tmp, x, y;
            scanf("%d %d %d", &tmp, &x, &y);
            if(x > n || y > n)
            {
                 ans++;
                continue;
            }
            if(x == y && tmp == 2)
            {
                 ans++;
                continue;
            }
            int cnt = (tmp == 1 ? 0 : 1);
            int u = Find(x), v = Find(y);
            if(u != v) ///在同一个集合
            {
                d[u] = ((d[y]) % 3 + cnt + 3 - (d[x] % 3)) % 3;
                pre[u] = v;
            }
            else ///在同一个集合
            {
                if(cnt == 0 && (abs(d[y] - d[x]) % 3 != 0)) ans++;
                if(cnt == 1 && !((d[x] - d[y]) % 3 == 1 || (d[y] - d[x]) % 3 == 2)) ans++;
            }
        }
        printf("%d
    ", ans);
        return 0;
    }
    Code

    牛客练习赛16

    A.字典序最大的子序列

    没仔细思考,就上手写代码,结果就是走了个大弯路。

    自己写的代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 1e5 + 50;
    char s[maxn];
    struct node
    {
        int c;
        int id;
        friend bool operator < (node A, node B)
        {
            if(A.c != B.c) return A.c > B.c;
            else return A.id < B.id;
        }
    };
    node cur[maxn];
    node tmp[maxn];
    int main()
    {
        scanf("%s", s);
        int len = strlen(s);
        for(int i = 0; i < len; i++)
        {
            cur[i].c = s[i] - 'a';
            cur[i].id = i;
        }
        int ans = 0;
        while(1)
        {
            memcpy(tmp, cur, sizeof(tmp));
            sort(tmp + ans, tmp + len);
            int maxv = tmp[ans].c;
            for(int i = ans; i < len; i++)
            {
                if(maxv == tmp[i].c)
                {
                    printf("%c", tmp[i].c + 'a');
                    ans = tmp[i].id + 1;
                }
                else break;
            }
            if(ans > len - 1) break;
        }
        puts("");
        return 0;
    }
    Code

    当自己写完后,发现了规律,就是最后答案的串,字符串的一定是从大到小的,比如bbba。

    看完某一的代码:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 1e5 + 50;
    char s[maxn];
    int main()
    {
        scanf("%s", s + 1);
        int n = strlen(s + 1);
        string ans = "";
        ans += s[n];
        for(int i = n - 1; i >= 1; i--)
        {
            if(s[i] >= ans[ans.length() - 1]) ans += s[i];
        }
        reverse(ans.begin(), ans.end());
        cout << ans << endl;
        return 0;
    }
    Code

    B漂亮的树

    这个题我一直想着用并查集来做:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 1e5 + 50;
    int pre[maxn];
    int d[maxn];
    int num[maxn];
    int Find(int x)
    {
        if(x == pre[x]) return x;
        int root = Find(pre[x]);
        d[x] += d[pre[x]];
        return pre[x] = root;
    }
    void merge1(int x, int y)
    {
        x = Find(x), y = Find(y);
        if(x != y)
        {
            pre[x] = y, d[x] = num[y];
            num[y] += num[x];
        }
    }
    struct node
    {
        int val;
        int id;
    };
    node cur[maxn];
    int main()
    {
        int n; scanf("%d", &n);
        for(int i = 1; i <= n; i++) pre[i] = i, d[i] = 0, num[i] = 1;
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &cur[i].val);
            cur[i].id = i;
        }
        for(int i = 1; i <= (n - 1) / 2; i++)
        {
            if(cur[i].val + 1 == cur[i + 1].val) merge1(i, i + 1);
        }
        for(int i = n; i > (n + 2) / 2; i--)
        {
            if(cur[i].val + 1 == cur[i - 1].val) merge1(i , i - 1);
        }
        for(int i = 1; i <= n / 2; i++)
        {
            if(cur[i].val == cur[n - i + 1].val) merge1(i, n - i + 1);
        }
        int maxv = 0;
        for(int i = 1; i <= n; i++)
        {
            //printf("%d
    ", num[i]);
            maxv = max(maxv, num[i]);
        }
        printf("%d
    ", n - maxv);
        return 0;
    }
    /*
    4
    1 2 2 3
    11
    1 2 3 8 5 7 6 5 4 2 1
    */
    Code

    我是把递增上升的符合规律的联成一个块,但是有一种情况没法解决, 1 2 3 8 5 7 6 5 4 2 1,就是1 2 3和8虽然不是等差1的递增规律,但和5是,我在想着先把小的合并,但是还是不行。

    看别人的题解,我发现我忽视了每个val和它的位置之间的关系,就是如果等差为1递增,val - i是相同的,以(n+1) / 2为例。

    比如上面的样例,是0 0 0 4 0 1 1 1 1 0 0,这样就相当于把5也合并到里面了。

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 1e5 + 50;
    map<int, int> vis;
    int a[maxn];
    
    int main()
    {
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
        for(int i = 1; i <= n / 2; i++)
        {
            a[i] -= i;
            a[n - i + 1] -= i;
            vis[a[i]]++;
            vis[a[n - i + 1]]++;
        }
        if(n % 2) a[(n + 1) / 2] -= (n + 1) / 2, vis[a[(n + 1) / 2]]++;
        int ans = 0;
        for(map<int, int>::iterator it = vis.begin(); it != vis.end(); it++)
        {
            ans = max(ans, it->second);
        }
        printf("%d
    ", n - ans);
        return 0;
    }
    Code

    C任意点

    联通块的个数 - 1。

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 1e2 + 50;
    int x[maxn], y[maxn];
    int pre[maxn];
    int Find(int x)
    {
        return x == pre[x] ? x : pre[x] = Find(pre[x]);
    }
    void merge1(int x, int y)
    {
        x = Find(x);
        y = Find(y);
        pre[x] = y;
    }
    int main()
    {
        int n; scanf("%d", &n);
        for(int i = 1; i <= n; i++)
        {
            pre[i] = i;
            scanf("%d %d", &x[i], &y[i]);
        }
        for(int i = 1; i <= n; i++)
        {
            for(int j = i + 1; j <= n; j++)
            {
                if(x[i] == x[j] || y[i] == y[j]) merge1(i, j);
            }
        }
        int cnt = 0;
        for(int i = 1; i <= n; i++)
        {
            if(pre[i] == i) cnt++;
        }
        printf("%d
    ", cnt - 1);
        return 0;
    }
    /*
    
    1 2 3 4 5
    */
    Code

    求值

    比赛的时候我想的是每个数字拆成20位来搞,用前缀和什么的搞一搞,想起去年西安赛区有道这样的题,稍后补一下,

    看了题解后明白了,实际上暴力做法n^2就是枚举区间,但是通过 | 运算,区间中通过或运算能产生不同的数的个数最多为20,可以用set来维护,总时间复杂度O(20log20*n) 某一还是sao,看了一个别人的题解,20位拆开考虑,代码贼长

    他的思路总体考虑:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 1e5 + 50;
    int a[maxn];
    set<int> ans, tmp, del;
    int main()
    {
        int n; scanf("%d", &n);
        for(int i = 1; i <= n; i++)
        {
            scanf("%d", &a[i]);
            ans.insert(a[i]);
            int res = a[i];
            for(set<int>::reverse_iterator it = tmp.rbegin(); it != tmp.rend(); it++)
            {
                if((a[*it] | res) != res) res |= a[*it], ans.insert(res);
                else del.insert(*it);
            }
            for(set<int>::iterator it = del.begin(); it != del.end(); it++)
            {
                tmp.erase(*it);
            }
            del.clear();
            tmp.insert(i);
        }
        printf("%d
    ", ans.size());
        return 0;
    }
    Code

    F选值

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 1e5 + 50;
    int a[maxn];
    typedef long long ll;
    int main()
    {
        int n, d; scanf("%d %d", &n, &d);
        for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
        ll ans = 0;
        for(int i = n; i >= 1; i--)
        {
            int j = lower_bound(a + 1, a + n + 1, a[i] - d) - a;
            if(j + 2 <= i) ans += (ll)(i - j - 1) * (i - j) / 2LL;
        }
        printf("%lld
    ", ans);
        return 0;
    }
    /*
    
    1 2 3 4 5
    */
    Code
  • 相关阅读:
    在SharePoint中实现Workflow(2):创建一个Workflow
    pku1384PiggyBank(动态规划)
    pku1088滑雪(记忆性搜索)
    hdu1251统计难题(初次接触字典树)
    详细解说 STL 排序(Sort)
    pku1631Bridging signals(动态规划题+二分搜索)
    pku1157LITTLE SHOP OF FLOWERS(简单动态规划题:摆放鲜花使审美价值达到最高)
    pku1067取石子游戏(博弈)
    pku2524Ubiquitous Religions(初次接触并查集)
    pku1050To the Max(求矩阵的最大子段和)
  • 原文地址:https://www.cnblogs.com/littlepear/p/8945707.html
Copyright © 2011-2022 走看看