2019江苏省赛
A.Cotreey
传送:http://acm.hdu.edu.cn/showproblem.php?pid=6567
题意:有一个$n$个点的树,$n-2$条边,给任意两点间加一条边,问点两两之间的距离之和最小为多少。
数据范围:$2<=n<=10^5$。
分析:读完题,学妹:这不是个树形dp+换根嘛?我:喵喵喵???(我不会啊。。
然后读了一会儿,,,emmmm不是求两个子树重心就好了嘛。
树的性质有:
- 树中所有点到某个点的距离和中,到重心的距离和是最小的,如果有两个距离和,他们的距离和一样。
- 把两棵树通过一条边相连,新的树的重心在原来两棵树重心的连线上。
- 一棵树添加或者删除一个节点,树的重心最多只移动一条边的位置。
- 一棵树最多有两个重心,且相邻。
所以,连接两棵树的重心,得到的距离和最小。
求解距离和的时候,考虑每条边的贡献即可。
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }
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 }