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

    水。。。

  • 相关阅读:
    终端-进入云服务器
    Git-简单的利用SourceTree提交代码
    iOS-多线程的底层实现
    JS-表单提交检查表单字数方法
    JS-实时修改在textarea里面的span(实际输入的文字)
    JS-textarea限制输入字数
    JS-Zepto.js中文链接
    搭建简单的单个Mybatis框架
    创建一个简单的SSM框架(2)
    创建一个简单的SSM框架(1)
  • 原文地址:https://www.cnblogs.com/meanttobe/p/11838253.html
Copyright © 2011-2022 走看看