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

水。。。

原文地址:https://www.cnblogs.com/meanttobe/p/11838253.html