10-8 DAIRY

前几天没写完的都是要么难以理解要么懒得打题了【工业】
今天也来划水~~~

今天的出题人何爷爷因为太帅(lan)气(duo)了,所以solution…不存在的(tan90°)
【hyy:我什么时候写过solution?】

T1 Graph 0
审题要仔细,做题更要仔细。不要在做题的时候就忘记了题目有两个小问!
很惨的爆0了一道真的就没有什么思考难度的题。

第一问:求最多的次数。
很明显就是每个连通块内边数>>1累加起来。

第二问:所以我就是写代码写着写着不记得要输出方案了。(毕竟是最后才开始写这道题)
也很明显,我们dfs时顺便就把方案记录一下。【完全ojbk…】
满分修改如下:

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>

using namespace std;

const int N = 100010;
const int M = 200010;

vector<int> V[N];
int n, m, ans, e, cnt;
int pre[N], fa[N];
int to[M<<1], nxt[M<<1], head[N];

template <typename T>
T read(){
    T N(0), F(1);
    char C = getchar();
    for(; !isdigit(C); C = getchar()) if(C == '-') F = -1;
    for(; isdigit(C); C = getchar()) N = N*10 + C-48;
    return N*F;
}

void add(int u, int v){
    to[++e] = v; nxt[e] = head[u]; head[u] = e;
    to[++e] = u; nxt[e] = head[v]; head[v] = e;
}

int find(int x){ return x == fa[x] ? x : fa[x] = find(fa[x]); }

void dfs(int u, int f = 0){
    pre[u] = ++cnt;
    for(int i = head[u]; i; i = nxt[i]){
        int v = to[i];
        if(v == f) continue;
        if(!pre[v]){
            fa[v] = u;
            dfs(v, u);
        }
        else if(pre[v] < pre[u]){
            V[u].push_back(v);
        }
    }
    if(fa[u]){
        if(V[u].size() & 1) V[u].push_back(fa[u]);
        else V[fa[u]].push_back(u);
    }
}

int main(){
    freopen("graph.in", "r", stdin);
    freopen("graph.out","w",stdout);

    n = read<int>(); m = read<int>();

    for(int i = 1; i <= m; i++){
        int u = read<int>();
        int v = read<int>();
        add(u, v);
    }

    for(int i = 1; i <= n; i++){
        if(!pre[i]) dfs(i);
    }

    for(int i = 1; i <= n; i++){
        ans += V[i].size() >> 1;
    }
    printf("%d\n", ans);
    for(int i = 1; i <= n; i++){
        for(int j = 1; j < V[i].size(); j += 2){
            printf("%d %d %d\n", V[i][j-1], i, V[i][j]);
        }
    }

    return 0;
}

T2 permutation 50
我也不知道一个完全没有正确性的做法怎么有50分的,但是据说连什么爆搜都搜出了90(笑)
我的考场想法是:
序列从前往后的确定每一个位置的最终数字。其实肯定不是最终的,(非正确做法)但我觉得能水过去一些点吧。这里我是怎么确定的呢,我不是往后>=k位一个个判断的,这样明显浪费时间。因为观察到是一个排列,所以数字和所在位置一定都是1~n的,这样我又开一个数组b[i]记录i这个数字当前所在位置。题目要求数值差==1。故每次只要判断比当前位数值小1的数值在哪个位置,这个位置是否与当前位相差>=k,符合就交换,不符合就视作已达到最终结果。

有一股贪心的味儿(#`O′)
正解我还没怎么弄明白,还是有些难度的。(记不太清hyy的讲解了)这个题思路需要转化一下。题目要求数值差为1,位置差>=k,可以转化为另一序列要求位置差为1,数值差

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int oo = 1000007;
const int N = 500010;

int n, k, x, y, tmp;
int a[N], p[N];

template <typename T>
T read(){
    T N(0), F(1);
    char C = getchar();
    for(; !isdigit(C); C = getchar()) if(C == '-') F = -1;
    for(; isdigit(C); C = getchar()) N = N*10 + C-48;
    return N*F;
}

int main(){
    freopen("permutation.in", "r", stdin);
    freopen("permutation.out","w", stdout);

    n = read<int>(); k = read<int>();
    for(int i = 1; i <= n; i++){
        a[i] = read<int>();
        p[a[i]] = i;
    }

    a[0] = oo;
    for(int i = 1; i <= n; i++){
        while(a[p[a[i]-1]] < a[p[a[i]]] && p[a[i]-1] - p[a[i]] >= k){
            x = a[i]-1, y = a[i];
            a[p[x]] = y; a[p[y]] = x;
            tmp = p[x]; p[x] = p[y]; p[y] = tmp;
        }
    }

    for(int i = 1; i <= n; i++) printf("%d\n", a[i]);
    return 0;
}

T3 tree 100
国家集训队论问题被众人猜出结论直接秒题,让出题人hyy哭晕在厕所。
这是一道集训队论问题,然而…并不是我能去集训队了,而是this结论题实在太走运了。我写了个5pts暴力打了几组数据发现answer全部是权值和。就大胆猜测,省去证明(不会),直接当作结论题写了两行代码。

莫名其妙A掉了/???

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

typedef long long ll;
const int N = 100010;
int n;
ll ans;

template <typename T>
T read(){
    T N(0), F(1);
    char C = getchar();
    for(; !isdigit(C); C = getchar()) if(C == '-') F = -1;
    for(; isdigit(C); C = getchar()) N = N*10 + C-48;
    return N*F;
}

int main(){
    freopen("tree.in", "r", stdin);
    freopen("tree.out","w",stdout);

    n = read<int>();
    for(int i = 1; i < n; i++){
        int u = read<int>(); int v = read<int>(); ll w = read<ll>();
        ans += w;
    }

    printf("%lld\n", ans);
    return 0;
}

考场写的5pts暴力如下,好像还有点问题,不过用来发现结论绰绰有余了(中间结果什么的没有注释掉,反正是用来找结论的):

#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

typedef long long ll;
const ll oo = 1e18+7;
const int N = 110;

ll sum, ans, pn, h;
int n, f;
int vis[N], p[N], res[N];
ll e[N][N], pth[N];

template <typename T>
T read(){
    T N(0), F(1);
    char C = getchar();
    for(; !isdigit(C); C = getchar()) if(C == '-') F = -1;
    for(; isdigit(C); C = getchar()) N = N*10 + C-48;
    return N*F;
}

void dfs(int s, int t){
    if(f) return;
    if(s == t){
        f = 1;
//      for(int i = 1; i <= pn; i++){ sum = min(sum, pth[i]); printf("%lld ", pth[i]);}
//      printf("\n");
        return;
    }
    for(int i = 1; i <= n; i++){
        if(e[s][i] != -1 && !vis[i]){
            vis[i] = 1;
            pth[++pn] = e[s][i];
            dfs(i, t);
            vis[i] = 0;
            pn--;
        }
    }
}

int main(){
    freopen("tree.in", "r", stdin);
    freopen("tree.out","w",stdout);

    n = read<int>();
    for(int i = 1; i <= n; i++) p[i] = i;
    for(int i = 1; i <= n; i++){
        for(int j = 1; j <= n; j++){
            e[i][j] = -1;
        }
    }
    for(int i = 1; i < n; i++){
        int u = read<int>();
        int v = read<int>();
        ll w = read<ll>();
        e[u][v] = e[v][u] = w;
    }

    do{
        h = 0LL;
        for(int i = 1; i < n; i++){
            int u = p[i];
            int v = p[i+1];

            memset(vis, 0, sizeof(vis));
            vis[u] = 1; f = pn = 0; sum = oo;
            dfs(u, v);
            h += sum;
        }
    //  printf("%lld\n", h);
//      ans = max(ans, h);
        if(ans < h){
            for(int i = 1; i <= n; i++) res[i] = p[i];
            ans = h;
        }
    }while(next_permutation(p+1, p+n+1));

    for(int i = 1; i <= n; i++) printf("%d ", res[i]);
    printf("\n");
    printf("%lld\n", ans);
    return 0;
}

证明其实也不难,不好写出来,感性理解一下。
首先显然ans的上界就是权值和,现在要证明ans的下界同样是权值和。
/*
一条边接A、B两点,必定不会从A->B->别的点->B->A->…
在经过A-B这条边前一定会把A那边一堆点包括什么循环回到A的点都走一遍,所以最佳方案不存在又绕回A这样的方案。
*/
我们把边权从小到大考虑,每次考虑一条边的时候,都把这棵树分成左右两棵树(两个子问题),而当前考虑的这条边必定会被经过,所以对答案的贡献必定会被算一次。划分成子问题考虑都是如此,所以每一条边都对答案至少产生一次贡献,所以ans必定是权值和。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值