2018 CCPC网络赛部分题解

版权声明:转载请注明原址,有错误欢迎指出 https://blog.csdn.net/iwts_24/article/details/82182271

听说莫干山一日游?比赛当天体验还是略差的,没想到hdu这么大的平台能出问题= =计蒜客可能笑出了声。不过估计还是凉了,就算体验极好自己太菜还是不行啊QAQ,听说省赛四省赛能补名额,弱校也有名额,希望能续上参加一波现场赛。

1001 Buy and Resell

1001 Buy and Resell

题解

        当时做出来的好像不多,感觉并没有那么难(然而还是不会)。这个题当时还是挺亏的= =,大概思路是一样,但是没有考虑优先队列,最后还是gg了。不过现在的写法确实有些蛋疼,感觉非常的绕。

        题目大概有n个城市,每次在一个城市可以购买一个物品花费ai或者卖出一个物品也是ai,求最后最大利润是多少,同时要求有最少的交易次数。整体是利用贪心,用优先队列维护购买列表,可以碰见价格低的就买,碰见高的就卖,然后可以构造出这样的情况:有序列1 2 5。首先在1买在2卖,这样赚了1块钱,变量ans记录净赚,值为1。到3的时候发现在3卖可以赚4块钱,而此时已经买一次了,但我们可以假设在2买了一次,那么就是2买3卖,净赚3元,此时对ans累加,ans是4元。这样,虽然在中间违反了规定,但是最终的结果是正确的,同时满足了贪心的算法。那么我们就可以这样来写代码,每次到中间的时候我们就记录,如果发现曾经买的不是最优,就减去一次交易次数。

        最后的代码实际上对于中间变量是存进去了2次,拿例子代入代码就可理解。

代码

#include<iostream>
#include<vector>
#include<list>
#include<string>
#include<cmath>
#include<algorithm>
#include<map>
#include<set>
#include<utility>
#include<queue>
#include<sstream>
#include<iterator>
#include<math.h>
#include<malloc.h>
#include<string.h>
#include<stack>
#include<functional>
#define TIME std::ios::sync_with_stdio(false)
#define LL long long
#define MOD 1000000007
#define MAX 1000001000
#define INF 0x3f3f3f3f

using namespace std;

int T,N;

int main() {
    TIME;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&N);
        priority_queue<int,vector<int>,greater<int>> q;
        map<int,int> step;
        LL ans = 0;
        LL times = 0;
        int number;
        for(int i = 0;i < N;i++){
            scanf("%d",&number);
            q.push(number);
            int less_n = q.top();
            if(less_n < number){
                ans += (number - less_n);
                times++;
                q.pop();
                q.push(number);
                if(step[less_n] > 0){
                    step[less_n]--;
                    times--;
                }
                step[number]++;
            }
        }
        printf("%lld %lld\n",ans,times*2);
    }

    system("pause");
    return 0;
}

1004 Find Integer

1004 Find Integer

题解

        费马大定理,简单说:满足a^n + b^n = c^n 这个等式,且a、b、c均为正整数的情况下,n只能为1和2。所以这个题就直接变成当n为1和2时的情况了。题目特判,写一种情况即可。n=1的时候,满足a+b=c即可,随便写。n=2的时候首先化简:

b^2 - c^2 = a^2 => (b+c)(b-c) = a^2

分为奇数和偶数情况。当a为奇数,可以这样推:

b+c = a^2,b-c = 1 => b = (a^2 + 1)/2

由于a为奇数,所以b必定为正整数,同时可得:c = a^2 - b

当a为偶数,可以这样推:

b+c = a^2/2,b-c=2=>b=(a^2/2 + 2)/2

由于a为偶数,所以b必定为正整数,同时可得:c=a^2/2 - b

AC 代码

#include<iostream>
#include<vector>
#include<list>
#include<string>
#include<cmath>
#include<algorithm>
#include<map>
#include<set>
#include<utility>
#include<queue>
#include<sstream>
#include<iterator>
#include<math.h>
#include<malloc.h>
#include<string.h>
#include<stack>
#define TIME std::ios::sync_with_stdio(false)
#define LL long long
#define MAX 510
#define INF 0x3f3f3f3f

using namespace std;

int T;

int main() {
    TIME;
    scanf("%d", &T);
    while (T--) {
        LL n;
        int a;
        scanf("%lld%d", &n, &a);
        if(n == 1){
            printf("%d %d\n",1,a+1);
        }else{
            if(n == 2){
                LL b,c;
                if(a % 2 == 0){
                    b = (a*a/2 + 2)/2;
                    c = a*a/2 - b;
                }else{
                    b = (a*a + 1)/2;
                    c = a*a - b;
                }
                if(b < c){
                    printf("%lld %lld\n",b,c);
                }else{
                    printf("%lld %lld\n",c,b);
                }
            }else{
                printf("-1 -1\n");
            }
        }
    }

    system("pause");
    return 0;
}

1009 Tree and Permutation

1009 Tree and Permutation

题解

        这个题跟树上距离有关,问题在于如何转换成为树上距离的问题。首先题目比较绕,大意是所有全排列后,到达每个结点的最短路的权值和。以题目给的第二个样例解释:

3个点全排列为:

1 2 3            w12 + w13

1 3 2            w13 + w12

2 1 3            w12 + w23

2 3 1            w23 + w12

......

像这样,最终求和。

        我们就可以发现规律,全排列的情况下任意2点的距离总共出现了2*(n-1)!次,那么求和的答案就是次数乘上任意2点距离和,也就是树上距离和。那么利用树状dp求出以后做乘法就完事了。这个出现的次数比较难算,要这样分析。例如w12的出现次数,在以1开头中,出现了(n-1)!次,在2开头中,w21与w12是等价的,也出现了同样次数,所以是2*(n-1)!次。

        这个题数据非常大,并且也要求对答案取模。这一点还是比较恶心的,感觉对这种需要取模的题,正常写能写出来,取模部分就经常错,并且还很难自己写数据测试。这个题就一直卡在一点,(n -1)!的取模,不能直接乘后再取模。所以应该在最初的时候利用num数组,算出所有阶乘,并且每次都要取模。这样预处理以后答案就正确了。

代码

#include<iostream>
#include<vector>
#include<list>
#include<string>
#include<cmath>
#include<algorithm>
#include<map>
#include<set>
#include<utility>
#include<queue>
#include<sstream>
#include<iterator>
#include<math.h>
#include<malloc.h>
#include<string.h>
#include<stack>
#define TIME std::ios::sync_with_stdio(false)
#define LL long long
#define MOD 1000000007
#define MAX 101000
#define INF 0x3f3f3f3f

using namespace std;

int N;
LL sum[MAX],dp[MAX],num[MAX];

struct node{
    int to;
    LL value;
};

vector<node> vec[MAX];

void init(){
    memset(dp,0,sizeof(dp));
    for(int i = 0;i <= N;i++){
        vec[i].clear();
        sum[i] = 1;
    }
}

void tree_dp(int root,int father){
    int len = vec[root].size();
    for(int i = 0;i < len;i++){
        int son = vec[root][i].to;
        if(son == father) continue;
        tree_dp(son,root);
        LL value = vec[root][i].value;
        sum[root] += sum[son];
        dp[root] = (dp[root] + dp[son] + (sum[son]*(N - sum[son])*value)%MOD)%MOD;
    }
}

int main() {
    TIME;
    num[1] = 1;
    for(int i = 2;i < MAX;i++){
        num[i] = (num[i-1]*i) % MOD;
    }
    while(scanf("%d",&N) != EOF){
        init();
        int a,b;
        LL c;
        for(int i = 0;i < N-1;i++){
            scanf("%d%d%lld",&a,&b,&c);
            vec[a].push_back(node{ b,c });
            vec[b].push_back(node{ a,c });
        }
        tree_dp(1,0);
        printf("%lld\n",((dp[1]*2)%MOD*num[N-1])%MOD);
    }

    system("pause");
    return 0;
}

 

猜你喜欢

转载自blog.csdn.net/iwts_24/article/details/82182271
今日推荐