zoukankan      html  css  js  c++  java
  • 2019CCPC-江西省赛

    A - Cotree

    原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=6567

    题目大意:

    给了两棵树,插入一条边,构成一个树,要求所有点的连线距离之和最小。

    解题思路:

    首先明确什么是重心。一棵树上的重心到这棵树上其他点的距离之和最短,如果让一颗树上加一个点使这个点到其他点的距离最短,那么这个点应该加在这个重心上,所以可以将一个树看作一个点,将这棵树加在重心上的得到的所有点间的距离之和最小。求距离之和也就是每条边的贡献之和,一条边的贡献和也就是它左右节点的乘积乘以权值。求贡献合时可以用求重心的思路,记录每条边的父亲端的所有节点个数之和,在它本身和儿子节点个数之和。最后计算相加即可。

    代码:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const ll N = 2e5;
     5 vector<ll>arr[N];
     6 bool vis[N];
     7 ll du[N];
     8 ll balan[N];
     9 ll x1 , n , x2 ,  s2 , s1 = 0 , ans = 0;
    10 ll balance1 = N,balance2 = N;
    11 
    12 void dfs1(ll u,ll fa){//求第一棵树的重心 
    13     ll v,sum = 0;
    14     vis[u] = 1 , s1 ++ ;//标记第一棵树的所有节点并记录个数
    15     du[u] = 1;
    16     for(ll i = 0;i < arr[u].size();i ++ ){
    17         v = arr[u][i];
    18         if(v == fa) continue;
    19         dfs1(v,u);
    20         du[u] += du[v];
    21         balan[u] = max(balan[u],du[v]);
    22     }
    23 }
    24 
    25 void dfs2(ll u,ll fa){//求第二棵树的重心 
    26     ll v,sum = 0;
    27     du[u] = 1;
    28     for(ll i = 0;i < arr[u].size();i ++ ){
    29         v = arr[u][i];
    30         if(v == fa) continue;
    31         dfs2(v,u);
    32         du[u] += du[v];
    33         sum = max(sum,du[v]);
    34     }
    35     sum = max(sum , s2 - du[u]);
    36     if(balance2 > sum){
    37         balance2 = sum;
    38         x2 = u;
    39     }
    40 }
    41 
    42 void dfs3(ll u , ll fa){//求所有点的距离之和 
    43     ll v , sum1 = 1 , sum2;
    44     du[u] = 1;
    45     for(ll i = 0;i < arr[u].size();i ++ ){
    46         v = arr[u][i];
    47         if(v == fa) continue;
    48         dfs3(v , u);
    49         du[u] += du[v];
    50         sum1 = sum1 + du[v];
    51     }
    52     sum2 = n - du[u];
    53     ans += (sum1 * sum2);
    54 }
    55 int main(){
    56     ll u,v;
    57     cin >> n;
    58     for(ll i = 0; i < n-2;i ++){
    59         cin >> u >> v;
    60         arr[u].push_back(v);
    61         arr[v].push_back(u);
    62     }
    63     dfs1(1,0);
    64     for(ll i = 1;i <= n;i ++ ){
    65         if(vis[i]){
    66             balan[i] = max(balan[i] , s1-du[i]);
    67             if(balance1 > balan[i]){
    68                 balance1 = balan[i];
    69                 x1 = i;
    70             }
    71         }
    72         else{
    73             u = i;
    74         }
    75     }
    76     s2 = n - s1;//第二棵树的节点个数 
    77     dfs2(u , 0);
    78     arr[x1].push_back(x2);
    79     arr[x2].push_back(x1);
    80     memset(du , 0 , sizeof(du));
    81     dfs3(1 , 0);
    82     cout << ans << endl;
    83     return 0;
    84 }

     

    D - Wave

    原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=6570

    题目大意:

    wave的定义是一个序列最少两个数字,序列中奇数位数字相同,偶数位的数字也相同,但奇数位与偶数位不同。给定一个数组,数组中元素在1到c之间,找到最长wave子序列。子序列的定义是不改变原序列的顺序,删除部分序列中元素的得到的序列。也就是说子序列中的元素在原序列中不一定是连在一起的。 

    解题思路:

    在这个序列中a,b组成的子序列的是a b a b a b a b,在原序列中可以递推出最长的子序列长度,dp[a][b]存储a与b组成的子序列的长度,在求dp[b][a]的时候,前n个序列中的dp[b][a]时,它是等于前n-1个序列中dp[a][b]+1,然后在读到第n个序列时更新一下dp数组,并记录最大值,最后输出最大值。

    代码:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 const int N=1e5+10;
     4 int arr[N];
     5 int dp[200][200];
     6 
     7 int solve(int n , int c ){
     8     int MAX = 0;
     9     for(int i = 0 ; i < n ; i ++ ){
    10         for(int j = 1 ; j <= c ; j ++ ){
    11             dp[arr[i]][j] = dp[j][arr[i]] + 1;
    12             if(arr[i] == j )
    13                 continue;
    14             MAX = max ( dp[arr[i]][j] , MAX );
    15         }
    16     }
    17     return MAX ;
    18 }
    19 
    20 int main(){
    21     int n,c;
    22     cin >> n >> c;
    23     for(int i = 0; i < n; i ++ ){
    24         scanf( "%d" , arr+i );
    25     }
    26     cout << solve(n , c) << endl;
    27     return 0;
    28 }

     

    F - String

    原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=6572

    题目大意:

    一个长度为n的字符串中选取四个字符,求构成"avin"的概率,输出最简分数。

     解题思路:

    第一个字符是'a'的概率是'a'出现的次数比上n,所以"avin"出现的概率是'a','v','i','n'出现的个数乘积比上pow(n,4)。化简用最大公约数即可。

    代码:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 int main(){
     5     int n,sum1,sum2,sum;
     6     string s;
     7     while(cin >> n >> s){
     8         map<char,int>mp;
     9         for(int i = 0;i < n;i ++ ){
    10             mp[s[i]] ++ ;
    11         }
    12         sum1=mp['a'] * mp['v'] * mp['i'] * mp['n'];
    13         sum2=n * n * n * n;
    14         if(sum1 == 0){
    15             puts("0/1");
    16         }
    17         else {
    18             int gcd = __gcd(sum1,sum2);
    19             sum1 = sum1 / gcd;
    20             sum2 = sum2 / gcd;
    21             cout << sum1 << "/" << sum2 << endl;
    22         }
    23     }
    24     return 0;
    25 }

     

    G - Traffic

    原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=6573

    题目大意:

    在十字路口有从南到北的汽车和从东到西的汽车,让所有从南到北的车等待n分钟,使得车可以顺利通过,求n的最小值。

    解题思路:

    数据太水,暴力即可。若增大数据就要用二分。

     

     

     

    H - Rng

    原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=6574

    题目大意:

    一个长度为n的数组,先随机找个右端点,再在1,r之间在找个左端点,用相同的方法再找到一组l,r。求两组范围交叉的概率。

    解题思路:

    求交叉过于复杂,所以要求不交叉的概率。

    如果r1 > r2,那么不交叉的概率为:(r1 - r2)/ r1

     

    所有的取法的个数是n*n,不交叉的情况是,右边区间的左端点大于左边区间的右端点。从r2 = 2开始遍历,得到等差数列前n-1项和 ( n * ( n - 1 ) ) / 2。所求概率为1 - ( ( n * ( n - 1 ) ) / 2 / ( n * n ) ),也就是 ( n + 1 )/ ( 2 * n ) % mod,用费马小定理计算逆元。

    代码如下:

     

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const ll mod=1e9+7;
     5 ll ksm(ll a,ll b){
     6     ll sum = 1;
     7     while( b ){
     8         if( b & 1 )    sum = sum * a % mod;
     9         a = a * a % mod;
    10         b >>= 1;    
    11     }
    12     return sum;
    13 }
    14 ll inv( ll n ){
    15     return ksm( n , mod - 2);
    16 }
    17 ll solve(ll n){
    18     return (n + 1) * inv( 2 * n ) % mod;
    19 }
    20 int main(){
    21     int n;
    22     while( cin >> n ){
    23         cout << solve( n ) << endl;
    24     }    
    25     return 0;
    26 }

     

     

    I - Budget

    原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=6575

     

     题目大意:

    给一组保留小数点后三位的小数,四舍五入保留到第二位,计算与原来的差值。

    解题思路:

    数据范围较大,用字符串存取,直接根据第三位小数计算结果即可。

     

     

    J - Worker

     原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=6576

     

    题目大意:

    有n个不同的工厂,m个工人,不同工厂中工人完成的订单量是不同的,给出你不同的工厂中每个人一天可以完成的订单量,计算是否可以有一种安排使得所有工厂完成的订单量相同。

    解题思路:

    求出每个工厂单个工人完成的订单量的最大公倍数,然后用这个公倍数对m取模。如果等于0代表可以分配成功,否则失败。

    代码:

     1 #include <bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 ll a[1000000];
     5 int main(){
     6     ll n,m;
     7     while(cin >> n >> m){
     8         ll  ans = 1,sum = 0;
     9         for(ll i = 0;i < n;i ++){
    10             scanf("%lld",a + i);
    11             ans=(ans * a[i] / (__gcd(ans,a[i])));
    12         }
    13         for(ll i = 0;i < n;i ++ ){
    14             sum = sum + ans / a[i];
    15         }
    16         if(m % sum == 0){
    17             puts("Yes");
    18             ll  flag = m / sum;
    19             for(ll i = 0;i < n;i ++ ){
    20                 if(i != 0){
    21                     cout << " ";
    22                 }
    23                 cout << (ans / a[i] * flag);
    24             }
    25             cout << endl;
    26         }
    27         else{
    28             puts("No");
    29         }
    30     }
    31     return 0;
    32 }

     

    K - Class

    水。。。

  • 相关阅读:
    POJ 3140 Contestants Division (树dp)
    POJ 3107 Godfather (树重心)
    POJ 1655 Balancing Act (树的重心)
    HDU 3534 Tree (经典树形dp)
    HDU 1561 The more, The Better (树形dp)
    HDU 1011 Starship Troopers (树dp)
    Light oj 1085
    Light oj 1013
    Light oj 1134
    FZU 2224 An exciting GCD problem(GCD种类预处理+树状数组维护)同hdu5869
  • 原文地址:https://www.cnblogs.com/meanttobe/p/11838253.html
Copyright © 2011-2022 走看看