(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
原文地址:https://www.cnblogs.com/littlepear/p/8945707.html