winter training 8 E F题解

 E题   

树上三角形  BZOJ3251  (斐波那契的性质+暴力)

题解:

若路径上有两点的点权为x,y

则若有个点z且z>abs(x-y)且z<x+y,则可以构成三角形

类似斐波那契数列1 2 3 5 8 13 。。。

发现最好情况下int范围只有不到50个点满足无法构成三角形

那么只要路径点超过50个直接输出Y,否则暴力。

类似的校赛上的一道题,HZNU2580也是这个思想。

#include<bits/stdc++.h>
using namespace std;
#define pi acos(-1.0)
#define inf 0x3f3f3f3f
typedef long long ll;
typedef double db;
int mo[4][2] = { -1,0,1,0,0,-1,0,1 };
//int mo[8][2] = { -1,0,1,0,-1,-1,1,1,0,-1,0,1,1,-1,-1,1 };
const int maxn = 2e5 + 10;
ll n, m, is = 0, t, c = 0, ans = 0, p[maxn], dep[maxn], fa[maxn], q[maxn];
vector< ll >v[maxn];
bool vis[maxn];
inline ll io() {
    ll sum = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')f = -1;
        ch = getchar();
    }
    while (ch <= '9' && ch >= '0') {
        sum = (sum << 1) + (sum << 3) + (ch ^ 48);
        ch = getchar();
    }
    return sum * f;
}
void dfs(int x) {
    for (int i = 0; i < v[x].size(); i++) {
        dep[v[x][i]] = dep[x] + 1;
        dfs(v[x][i]);
    }
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    cout << fixed << setprecision(2);
    n = io(), m = io();
    for (int i = 1; i <= n; i++)p[i] = io();
    for (int i = 1; i < n; i++) {
        ll a = io(), b = io();
        v[a].push_back(b);
        fa[b] = a;
    }
    dep[1] = 1;
    dfs(1);
    while (m--) {
        ll a, b, op;
        scanf("%lld%lld%lld", &op, &a, &b);
        if (op)p[a] = b;
        else {
            is = 0;
            int cnt = 0;
            if (dep[a] < dep[b])swap(a, b);
            while (dep[a] > dep[b]) {
                q[++cnt] = p[a];
                a = fa[a];
                if (cnt >= 50) {
                    is = 1;
                    break;
                }
            }
            while (a != b) {
                q[++cnt] = p[a];
                q[++cnt] = p[b];
                a = fa[a];
                b = fa[b];
                if (cnt >= 50) {
                    is = 1;
                    break;
                }
            }
            q[++cnt] = p[a];
            if (cnt >= 50)is = 1;
            sort(q + 1, q + 1 + cnt);
            for (int i = 3; i <= cnt; i++) {
                if (q[i - 2] + q[i - 1] > q[i]) {
                    is = 1;
                    break;
                }
            }
            if (is)printf("Y\n");
            else printf("N\n");
        }
    }
    return 0;
}

 

 

F题:

Buy or Build   UVA1151 (最小生成树+状压枚举)

题意:平面上有N个点(1≤N≤1000),若要新建边,费用是2点的欧几里德距离的平方。另外还有Q个套餐,每个套餐里的点互相联通,总费用为Ci。问让所有N个点连通的最小费用。(2组数据的输出之间要求有换行)

题解:这是《算法竞赛入门经典》P368上的例题。q很小,由于枚举量为O(2^q),给边排序的时间复杂度为O(n^2logn),而排序之后每次kruskal算法的时间复杂度为O(n^2),因此总时间复杂度为O(2^qn^2+n^2logn),对于题目来说的规模太大了。只需一个小小的优化即可降低时间复杂度:先求一次原图 (不购买任何套餐) 的最小生成树,得到n-1条边,其余的边就没用了。然后枚举买哪些套餐,则枚举套餐后再求最小生成树时,图上的边已经寥寥无几。在枚举的时候注意要用到状压。

#include<bits/stdc++.h>
using namespace std;
#define pi acos(-1.0)
#define inf 0x3f3f3f3f
typedef long long ll;
typedef double db;
int mo[4][2] = { -1,0,1,0,0,-1,0,1 };
//int mo[8][2] = { -1,0,1,0,-1,-1,1,1,0,-1,0,1,1,-1,-1,1 };
const int maxn = 4010;
int n, m, is = 0, t, c = 0, p[maxn];
struct xx {
    int ch;
    int q[maxn];
    int g;
}xx[10];
struct node {
    int x, y;
}d[maxn];
struct edge {
    int f, t, w;
}edge[maxn * maxn];
int find(int x) {
    if (x == p[x])return x;
    return p[x] = find(p[x]);
}
bool cmp(struct edge a, struct edge b) { return a.w < b.w; }
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    cout << fixed << setprecision(2);
    scanf("%d", &t);
    while (t--) {
        scanf("%d%d", &n, &m);
        c = 0;
        for (int i = 1; i <= m; i++) {
            int ch, a;
            scanf("%d%d", &a, &ch);
            xx[i].ch = ch;
            xx[i].g = a;
            for (int k = 1; k <= a; k++)scanf("%d", &xx[i].q[k]);
        }
        for (int i = 1; i <= n; i++)scanf("%d%d", &d[i].x, &d[i].y);
        for (int i = 1; i <= n; i++) {
            for (int k = i + 1; k <= n; k++) {
                edge[++c].w =
                    (d[i].x - d[k].x) * (d[i].x - d[k].x) +
                    (d[i].y - d[k].y) * (d[i].y - d[k].y);
                edge[c].f = i;
                edge[c].t = k;
            }
        }
        ll ans = 1e18;
        sort(edge + 1, edge + 1 + c, cmp);
        for (int i = 0; i < (1 << m); i++) {
            int cnt = 0;
            ll sum = 0;
            for (int k = 1; k <= n; k++)p[k] = k;
            for (int k = 1; k <= m; k++) {
                if (i & (1 << (k - 1))) {
                    sum += xx[k].ch;
                    int x = find(xx[k].q[1]);
                    for (int j = 2; j <= xx[k].g; j++) {
                        int y = find(xx[k].q[j]);
                        if (x != y)p[y] = x, ++cnt;
                    }
                }
            }
            for (int i = 1; cnt < n - 1; i++) {
                int x = find(edge[i].f), y = find(edge[i].t);
                if (x != y)sum += edge[i].w, p[x] = y, ++cnt;
            }
            ans = min(ans, sum);
        }
        printf("%lld\n", ans);
        if (t)printf("\n");
    }
    return 0;
}

 

posted @ 2020-02-05 14:22  powerkeke  阅读(160)  评论(0)    收藏  举报