2019江苏省赛

A.Cotreey

传送:http://acm.hdu.edu.cn/showproblem.php?pid=6567

题意:有一个$n$个点的树,$n-2$条边,给任意两点间加一条边,问点两两之间的距离之和最小为多少。

数据范围:$2<=n<=10^5$。

分析:读完题,学妹:这不是个树形dp+换根嘛?我:喵喵喵???(我不会啊。。

然后读了一会儿,,,emmmm不是求两个子树重心就好了嘛。

树的性质有:

  1. 树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个距离和,他们的距离和一样。
  2. 把两棵树通过一条边相连,新的树的重心在原来两棵树重心的连线上。
  3. 一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。
  4. 一棵树最多有两个重心,且相邻。

所以,连接两棵树的重心,得到的距离和最小。

求解距离和的时候,考虑每条边的贡献即可。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int maxn=1e5+10;
 5 vector<int> mp[maxn];
 6 int mi1,mi2,id1,id2,num1,num2;
 7 int dp[maxn],vis[maxn],n;
 8 ll num[maxn];
 9 ll ans;
10 void dfs(int x,int fa){
11     vis[x]=1;
12     for (int i=0;i<mp[x].size();i++){
13         if (mp[x][i]==fa) continue;
14         dfs(mp[x][i],x);
15     }
16 }
17 void dfs1(int x,int fa){
18     dp[x]=1;
19     int mx=0;
20     for (int i=0;i<mp[x].size();i++){
21         if (mp[x][i]==fa) continue;
22         dfs1(mp[x][i],x);
23         dp[x]+=dp[mp[x][i]];
24         mx=max(mx,dp[mp[x][i]]); 
25     }
26     mx=max(mx,num1-dp[x]);
27     if (mx<mi1){
28         mi1=mx;
29         id1=x;
30     }
31 }
32 void dfs2(int x,int fa){
33     dp[x]=1; vis[x]=1;
34     int mx=0;
35     for (int i=0;i<mp[x].size();i++){
36         if (mp[x][i]==fa) continue;
37         dfs2(mp[x][i],x);
38         dp[x]+=dp[mp[x][i]];
39         mx=max(mx,dp[mp[x][i]]); 
40     }
41     mx=max(mx,num2-dp[x]);
42     if (mx<mi2){
43         mi2=mx;
44         id2=x;
45     }
46 }
47 void dfs3(int x,int fa){
48     num[x]=1;
49     for (int i=0;i<mp[x].size();i++){
50         if (mp[x][i]==fa) continue;
51         dfs3(mp[x][i],x);
52         num[x]+=num[mp[x][i]];
53     }
54 }
55 void dfsans(int x,int fa){
56     for (int i=0;i<mp[x].size();i++){
57         if (mp[x][i]==fa) continue;
58         dfsans(mp[x][i],x);
59         ans+=1ll*(num[mp[x][i]]*(n-num[mp[x][i]]));
60     }
61 }
62 int main(){
63     int x,y,rt;  scanf("%d",&n);
64     for (int i=0;i<n-2;i++){
65         scanf("%d%d",&x,&y);
66         mp[x].push_back(y);
67         mp[y].push_back(x);
68         vis[i]=0; num[i]=0;
69     }
70     dfs(1,0);
71     num1=0; for (int i=1;i<=n;i++) if (vis[i]) num1++;
72     num2=n-num1;
73     mi1=2*n,mi2=2*n;
74     dfs1(1,0);
75     for (int i=1;i<=n;i++) if (!vis[i]){rt=i; break;}
76     dfs2(rt,0);
77     //cout << id1 << "   " << id2<< endl;
78     dfs3(id1,0);
79     dfs3(id2,0);
80     //cout << num[id1] << "   " << num[id2] << endl;
81     ans=0;
82     dfsans(id1,0); dfsans(id2,0);
83     ans+=1ll*(num[id1]*num[id2]);
84     printf("%lld\n",ans);
85     return 0;
86 }
A

B.Math

传送:http://acm.hdu.edu.cn/showproblem.php?pid=6568

 

C.Trap

传送:http://acm.hdu.edu.cn/showproblem.php?pid=6569

题意:有$n$个长度为$a_i$的线段,问可以构成等腰梯形的方案数为多少。且等腰梯形的四条边的$gcd=1$,全等的两个等腰梯形不重复计算。

数据范围:$1<=n<=2000,2<=a_i<=10000$。

分析: 要求四条边的$gcd=1$,那么就先考虑有多少的情况是满足的。

暴力枚举上下底$xy,$,然后再枚举腰$aa$,满足gcd(x,y,aa)=1 && num[aa]>=2。同时构成等腰梯形还有一个要求是腰的长度不小于上下底差的一半。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=2010;
 4 int a[maxn],id[maxn*10];
 5 int aa[maxn][maxn];
 6 vector<int> g[maxn*10];
 7 set<int> s;
 8 map<int,int> mp;
 9 int gcd_(int x,int y){
10     if(y==0) return x;
11     else return gcd_(y,x%y);
12 }
13 int solve(int id,int aa,int x,int y){
14     auto it=lower_bound(g[id].begin(),g[id].end(),aa);
15     if (it==g[id].end()) return 0;
16     int res=it-g[id].begin();
17     res=g[id].size()-res;
18     if (mp[x]==2){
19         int tmp=*lower_bound(g[id].begin(),g[id].end(),x);
20         if (tmp==x && tmp>=aa) res--;
21     } 
22     if (mp[y]==2){
23         int tmp=*lower_bound(g[id].begin(),g[id].end(),y);
24         if (tmp==y && y>=aa) res--;
25     }
26     return res;
27 }
28 int main(){
29     int n;
30     while (~scanf("%d",&n)){
31         s.clear();mp.clear();
32         for (int i=1;i<=n;i++){
33             scanf("%d",&a[i]);
34             mp[a[i]]++;
35         }
36         sort(a+1,a+1+n);
37         n=unique(a+1,a+1+n)-(a+1);
38         for (int i=1;i<=n;i++){
39             for (int j=i+1;j<=n;j++){
40                 aa[i][j]=gcd_(a[i],a[j]);
41                 s.insert(aa[i][j]);
42             }
43         }
44         int tot=0;
45         for (auto i:s) id[i]=++tot; 
46         for (int i=1;i<=n;i++){
47             for (auto j:s){
48                 int tmp=gcd_(a[i],j);
49                 if (tmp==1 && mp[a[i]]>=2){
50                     g[id[j]].push_back(a[i]);
51                 }
52             }
53         }
54         int ans=0;
55         for (int i=1;i<=n;i++){//上底 
56             for (int j=i+1;j<=n;j++){//下底 
57                 int d=(a[j]-a[i])/2+1;
58                 int tmp=solve(id[aa[i][j]],d,a[i],a[j]);
59                 (ans+=tmp); 
60             }
61         }
62         printf("%d\n",ans);
63         for (int i=1;i<=tot;i++) g[i].clear();
64     }
65     return 0;
66 } 
C

D.Wave

传送:http://acm.hdu.edu.cn/showproblem.php?pid=6570

题意:给一个$n$个数的序列,包含$1-c$范围内的数,要求求解一个序列要求如下:

奇数位数字相同;偶数位数字相同;奇偶位不同。问,这样的序列最长的为多长?

数据范围:$1<=n<=10^5,1<=c<=100$。

分析:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=1e5+7;
 4 struct node{int num,cnt;} dp[110][110];
 5 int a[maxn];
 6 int main()
 7 {
 8     int n,c;
 9     while (~scanf("%d%d",&n,&c))
10     {
11         for (int i=1;i<=n;i++) scanf("%d",&a[i]);
12         for (int i=1;i<=c;i++)
13             for (int j=1;j<=c;j++) dp[i][j].cnt=0,dp[i][j].num=i;
14         for (int i=1;i<=n;i++)
15         {
16             int x=a[i];
17             for (int i=1;i<=c;i++)
18             {
19                 if(x==i) continue;
20                 if(dp[i][x].num==x) {dp[i][x].cnt++;dp[i][x].num=i;}
21                 if(dp[x][i].num==x) {dp[x][i].cnt++;dp[x][i].num=i;}
22             }
23         }
24         int ans=0;
25         for (int i=1;i<=c;i++)
26             for (int j=1;j<=c;j++) ans=max(ans,dp[i][j].cnt);
27         printf("%d\n",ans);
28     }
29     return 0;
30 }
D

E.Packing

传送:http://acm.hdu.edu.cn/showproblem.php?pid=6571

 

F.String

传送:http://acm.hdu.edu.cn/showproblem.php?pid=6572

题意:给你一个只包含$a,v,i,n$的长度为$n$的字符串,问依此选取$a,v,i,n$的概率是多少。

数据范围:$1<=n<=100$。

分析:乘法原理。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 map<int,int> mp;
 4 int num[5];
 5 int gcd_(int xx,int yy){
 6     if (yy==0) return xx;
 7     else return gcd_(yy,xx%yy);
 8 }
 9 int main(){
10     int n;char ch; mp['a']=1; mp['v']=2; mp['i']=3; mp['n']=4;
11     while (~scanf("%d",&n)){
12         getchar();
13         memset(num,0,sizeof(num));
14         for (int i=1;i<=n;i++){
15             scanf("%c",&ch);
16             num[mp[ch]]++;
17         }
18         int xx=1,yy=n*n*n*n;
19         for (int i=1;i<=4;i++){
20             xx*=num[i];
21         }
22         if (xx==0){
23             printf("0/1\n");
24             continue;
25         }
26         int tmp=gcd_(xx,yy);
27         xx/=tmp; yy/=tmp;
28         printf("%d/%d\n",xx,yy);
29     }
30     return 0;
31 }
F

G.Traffic

传送:http://acm.hdu.edu.cn/showproblem.php?pid=6573

题意:东西向有$n$辆车,南北向有$m$辆车,他们通过路口时时间分别为$a_i,b_i$。问南北向的车最少等待多久可以保证没有车相撞。

数据范围:$1<=n,m<=1000,1<=a_i, b_i<=1000$。

分析:枚举等待时间,满足条件即break。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=1010;
 4 int a[maxn],b[maxn],c[maxn*2];
 5 int n,m;
 6 int main() {
 7     while (~scanf("%d%d",&n,&m)){
 8         for (int i=1;i<=n;i++) scanf("%d",&a[i]);
 9         for (int i=1;i<=m;i++) scanf("%d",&b[i]);
10         sort(a+1,a+1+n);
11         sort(b+1,b+1+m);
12         int kk=0;
13         while (1){
14             int num=0;
15             for (int i=1;i<=n;i++) c[++num]=a[i];
16             for (int i=1;i<=m;i++) c[++num]=b[i]+kk;
17             sort(c+1,c+1+num);
18             int m=unique(c+1,c+1+num)-(c+1);
19             if (m==num) break;
20             else kk++;
21         }
22         printf("%d\n",kk);
23     }
24     return 0;
25 }
G

H.Rng

传送:http://acm.hdu.edu.cn/showproblem.php?pid=6574

题意:在$1-n$范围内先选取区间的右界$r$,再在$1-r$内选取左界$l$。问任取两个区间相交的概率为多少。

数据范围:$1<=n<=10^6$。

分析:容斥原理。分析不相交的概率为多少。

和学妹们手推QAQ.

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int mod=1e9+7;
 5 ll pow_(ll x,int y){
 6     ll res=1,base=1ll*x;
 7     while (y){
 8         if (y&1) (res*=base)%=mod;
 9         (base*=base)%=mod;
10         y>>=1;
11     }
12     return res;
13 }
14 int main(){
15     int n;
16     while (~scanf("%d",&n)){
17         ll ans=(1ll*(n+1)*pow_(2ll*n,mod-2))%mod;
18         printf("%lld\n",ans);
19     }
20     return 0;
21 } 
H

I.Budget

传送:http://acm.hdu.edu.cn/showproblem.php?pid=6575

题意:有$n$个三位小数,要求四舍五入到两位小数,每次取四舍五入后的数与原数的差值,问全部差值的和为多少。

数据范围:$1<=n<=1000$。

分析:签到。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=1e5+7;
 4 int main()
 5 {
 6     int n;
 7     while (~scanf("%d",&n))
 8     {
 9         string s;
10         double ans=0;
11         while (n--)
12         {
13             cin>>s;
14             int st=s.length(),f;
15             for (int i=0;i<st;i++) 
16                 if(s[i]=='.'){f=i;break;}
17             int x=s[f+3]-'0';
18             if(x>=5) ans+=10-x;
19             else ans-=x;
20         }
21         ans/=1000.0;
22         printf("%.3f\n",ans);
23     }
24     return 0;
25 }
I

J.Worker

传送:http://acm.hdu.edu.cn/showproblem.php?pid=6576

题意:有$n$个工作间,$m$个工人,每个工作间每天每人能完成的工作量为$a_i$,问能否将这$m$个人分配到这$n$个工作间,要求每个工作间每天能完成的工作量相同。可以分配输出每个工作间分配的人数。

数据范围:$1<=n<=1000,1<=m<=10^{18}$。

分析:考虑所有$a_i$的最小公倍数,然后得到每个工作间人数比例,再判断是否可分。签到++。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 const int maxn=1010;
 5 ll a[maxn],b[maxn];
 6 ll gcd_(ll xx,ll yy){
 7     if (yy==0) return xx;
 8     else return gcd_(yy,xx%yy);
 9 }
10 int main(){
11     ll n,m;
12     while (~scanf("%lld%lld",&n,&m)){
13         for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
14         ll tmp=a[1];
15         for (int i=2;i<=n;i++){
16             ll kk=gcd_(a[i],tmp);
17             tmp=tmp/kk*a[i];
18         }
19         ll sum=0;
20         for (int i=1;i<=n;i++){
21             b[i]=tmp/a[i];
22             sum+=b[i];
23         }
24         if (m%sum!=0){
25             printf("No\n");
26             continue;
27         }
28         printf("Yes\n"); 
29         ll t=m/sum;
30         printf("%lld",t*b[1]);
31         for (int i=2;i<=n;i++){
32             printf(" %lld",t*b[i]);
33         }
34         printf("\n");
35     }
36     return 0;
37 }
J

K.Class

传送:http://acm.hdu.edu.cn/showproblem.php?pid=6577

题意:已知$x=a+b,y=a-b$。求解$a*b$。

分析:$a=\frac{x+y}{2},b=\frac{x-y}{2}$。签到++。

1 #include<bits/stdc++.h>
2 using namespace std;
3 int main(){
4     int x,y;scanf("%d%d",&x,&y);
5     int tmp=(x*x-y*y)/4;
6     printf("%d\n",tmp);
7     return 0;
8 } 
K

 

 

posted @ 2019-07-21 23:35  Changer-qyz  阅读(331)  评论(0编辑  收藏  举报