2019CCPC江西省賽

A-Cotree

題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=6567

題目大意:有兩棵樹,在兩個樹間連一條邊,求兩兩節點之間的距離和

解題思路:兩棵樹分別稱爲a,b樹,在a,b上分別找一點連接,其實就是找a,b樹的重心。
可以先分別求出a,b樹內部點距離的和,再找到重心,然後再通過重心求a樹上的點到b樹上的點的距離和。

推薦題解:https://blog.csdn.net/lalala445566/article/details/97004819

Code:

#include<iostream>
#include<cstring>

using namespace std;
typedef long long ll;
const int N = 5e6+5;
ll h[N],vis[N],cnt;
ll sum[N],sum_num[N];//以該節點爲根的樹的所有節點到該節點距離的和,,以該節點爲根的樹的所有子節點 
ll root_1,root_2,sum_root1,sum_root2;

struct Edge{
	ll to,nxt,w;
}eg[N*2]; 

void add(ll u,ll v){//鏈式前向星建圖 
	eg[cnt].w=1;eg[cnt].to=v;eg[cnt].nxt=h[u];h[u]=cnt++;
}

void dfs(ll x){//找到以每個節點爲根節點的樹的sum值和sum_num值 
	ll num=0;
	ll num_num=0;
	for(ll i=h[x];i!=-1;i=eg[i].nxt){
		ll v = eg[i].to;
		if(!vis[v]){
			vis[v]=1;
			dfs(v);
			num+=sum_num[v]+sum[v]+1;
			num_num+=sum_num[v]+1;
		}
	}
	sum[x]=num;
	sum_num[x]=num_num;
}

ll find_root(ll now,ll &root,ll &sum_root){//now爲當前節點,root一直更新爲當前最有可能是重心的節點,sum_root所有點到root距離的和 
	ll tot=0;
	tot+=sum[now];//將以所有節點爲根的sum值累加 
	ll temp=sum[now];//記憶當前節點的sum值和sum_num值,以便恢復現場 
	ll temp_root=sum_num[now];
	for(ll i=h[now];i!=-1;i=eg[i].nxt){//鏈式前向星遍歷圖 
		ll v=eg[i].to;
		if(!vis[v]){
			vis[v]=1;
			//****更新數組,以v爲重心是否比以root爲重心距離更小 
			sum[now]-=(sum_num[v]+sum[v]+1); 
			sum_num[now]-=(sum_num[v]+1);
			sum[v]+=(sum[now]+sum_num[now]+1);
			sum_num[v]+=(sum_num[now]+1);
			if(sum[v]<sum_root){//如果滿足條件,則更新重心及所有節點到重心的距離 
				sum_root=sum[v];
				root=v;
			} 
			tot+=find_root(v,root,sum_root);
			sum[now]=temp;//恢復現場 
			sum_num[now]=temp_root;
		}
	}
	return tot;
}

int main(){
	memset(h,-1,sizeof(h));
	memset(vis,0,sizeof(vis));
	cnt=0;
	ll n,ans=0;
	scanf("%lld",&n);
	ll u,v;
	for(int i=1;i<=n-2;i++){
		scanf("%lld%lld",&u,&v);
		add(u,v);add(v,u);
	}
	root_1=1;
	vis[1]=1;
	dfs(1);//先找一棵樹上的sum值和sum_num值 
//	cout<<"*************\n";
	sum_root1=sum[root_1];
	for(int i=1;i<=n;i++){//找另外一棵樹 
		if(!vis[i]){
			vis[i]=1;
			root_2=i;
			dfs(i);
			break;
		}
	}
//	cout<<"*************\n";
	sum_root2=sum[root_2];
	memset(vis,0,sizeof(vis));//將訪問記錄歸零 
	vis[root_1]=1;
	ll temp=find_root(root_1,root_1,sum_root1);//temp所存的值是該棵樹上以所有節點爲根的sum值的總和 ,即該樹上所有兩點距離和的兩倍,不明白可以模擬一下 
	ans+=temp/2;//內部距離和除以二,即爲該樹內部距離和 
	vis[root_2]=1; 
	temp=find_root(root_2,root_2,sum_root2);//同理 
	ans+=temp/2;
	ans+=((sum[root_1]+sum_num[root_1]+1)*(sum_num[root_2]+1)+sum[root_2]*(sum_num[root_1]+1)); //前半部分是所有a樹所有節點到b樹重心的距離和乘以b樹節點總個數 
																								//後半部分是b樹內部距離和乘以a樹節點個數 
	cout<<ans<<endl;
	
	return 0;
}

D.Wave

題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=6570

題目大意:在字符串s中求奇數位置爲i,偶數位置爲j切i,j不相等的子序列最長爲多少

解題思路:dp[i] [j] 存的是奇數位爲i偶數爲爲j的最長長度。對於每個a[i],更新dp[a[i]][j]爲偶數時的情況,更新dp[j][a[i]]爲奇數時的情況。因爲以a[i],j爲循環節長度是偶數才能在後面再加一個a[i],以j,a[i]爲循環節長度是奇數才能在後面再加一個a[i];

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int dp[200][200],s[100010];
int main()
{
	int n,c;
	scanf("%d %d",&n,&c);
	memset(dp,0,sizeof dp);
	for(int i=0;i<n;i++)
	{
		scanf("%d",&s[i]);
		for(int j=1;j<=c;j++)
		{
			if(j==s[i]) continue;
			if(dp[s[i]][j]%2==0) 
				dp[s[i]][j]++;
			if(dp[j][s[i]]&1) //當時用的else if 所以遍歷樣例到2時,dp[1][2]就連不上了 
				dp[j][s[i]]++;	
		}
		
	}
	int ans=0;
	for(int i=1;i<=c;i++)
		for(int j=1;j<=c;j++)
		{
			if(j==i) continue;
			ans=max(ans,dp[i][j]);
		}
	cout<<ans<<endl;
	return 0;
 }

F - String

題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=6572

題目大意:在字符串s中隨機抽四個字母,求四個字母組合是avin的概率

解題思路:遍歷s求字符串中a,v,i,n的個數,求出他們的乘積,再求出字符串長度的四次方,然後兩者約分按格式輸出即可

#include<iostream>
#include<map>
#include<algorithm>

using namespace std;
map<char,int>ma;

int main(){
	int n;
	string s;
	cin>>n>>s;
	for(int i=0;i<n;i++){
		ma[s[i]]++;
	}
	int a=ma['a'],b=ma['v'],c=ma['i'],d=ma['n'];
	int ans,res;
	ans=a*b*c*d;
	res=n*n*n*n;
	int temp=__gcd(ans,res);
	if(ans==0)
		printf("%d/%d\n",ans,1);
	else printf("%d/%d\n",ans/temp,res/temp);                                                                                                                                                                        
	
	return 0;
}

G - Traffic

題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=6573

題目大意:有從東到西行駛的車和從南到北行駛的車要使不發生車禍,求從南到北行駛的車最短停留時間

解題思路:如果要等,所有從南到北行駛的車都要等待相同的時間才能走,暴力枚舉1到1000,符合條件break輸出即可

#include<iostream>
#include<algorithm>
using namespace std;
int n,m;
int a[1050],b[1050],c[1050];

bool check(int x){
	for(int i=0;i<m;i++) c[i]=b[i];
	for(int i=0;i<m;i++) c[i]+=x;
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++){
			if(a[i]==c[j]) return false;
		}
	}
	return true;
}

int main(){
	cin>>n>>m;
	for(int i=0;i<n;i++) cin>>a[i];
	for(int j=0;j<m;j++) cin>>b[j];
	sort(a,a+n);
	sort(b,b+m);
	int ans;
	for(int i=0;i<=1000;i++){
		if(check(i)){
			ans=i;
			break;
		}
	}
	cout<<ans<<endl;
	
	return 0;
}

H - Rng

題目鏈接:u.edu.cn/showproblem.php?pid=6574

題目大意,在[1,c]中,找一個整數r,再從[1,r]中找一個整數l,變爲一個新範圍[l,r],這樣重複兩次問兩次範圍相交的概率

解題思路:打表找規律,發現規律爲(n+1)/(2*n),因爲數比較大,需要用到逆元

#include<iostream>

using namespace std;
typedef long long ll;
const ll mod = 1e9+7;

ll ksm(ll a,ll b){
	ll res=1;
	while(b){
		if(b&1) res=(res*a)%mod;
		a=a*a%mod;
		b>>=1;
	}
	return res;
}

int main(){
	ll n;
	cin>>n;
	cout<<(n+1)%mod*ksm(2*n,mod-2)%mod<<'\n';
	
	
	return 0;
}

I - Budget

題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=6575

題目大意:將公司所有賬目從三位小數保留成兩位,問更新後差值是多少

解題思路:將差額求出來輸出即可

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;
int n;
char p[20];

int main(){
	scanf("%d",&n);
	double ans=0;
	while(n--){
		scanf("%s",p);
		int len=strlen(p);
		int temp=p[len-1]-'0';
		if(temp<=4) {
			double t=temp*1.0/1000;
			ans-=t;
		}
		else {
			double t=temp*1.0/1000;
			ans+=(0.01-t);
		}
	}
	double pp;
//	ans=4.004;
	if(ans<0) pp=-ans;
	else pp=ans;
	if(pp>=0&&pp<=1.0) printf("%.3lf\n",ans);
	else if(pp>1.0){
		int num=(int)(pp*1000);
		int cnt=num%10;
		if(cnt<=4) printf("%.2lf\n",ans);
		else{
			if(ans<0) printf("%.2lf\n",ans-0.01);
			else printf("%.2lf\n",ans+0.01);
		}
	}
	
	return 0;
}

J - Worker

題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=6576

題目大意:有n個工廠m個工人,每個工廠產出不同,問怎樣分配工人使每個工廠產出相同

解題思路:找到工廠產出的最小公倍數,然後求出滿足相同產出的最少工人數,然後求出總工人對最少工人數的倍數,然後求出每個工廠所需最少工人數乘以倍數即爲每個工廠分配工人數,輸出即可

#include<iostream>
#include<algorithm>

using namespace std;
typedef long long ll;
const int N = 1005;
ll a[N],b[N];
ll n,m;

int main(){
	cin>>n>>m;
	ll ans=1;
	for(ll i=0;i<n;i++){
		cin>>a[i];
		ans=ans*a[i]/__gcd(ans,a[i]);
	}
	ll sum=0;
	for(ll i=0;i<n;i++){
		b[i]=ans/a[i];
		sum+=b[i];
	}
	if(m%sum==0) {
		puts("Yes");
		ll t=m/sum;
		for(ll i=0;i<n;i++){
			if(i) printf(" %lld",b[i]*t);
			else printf("%lld",b[i]*t);
		}
		puts("");
	}
	else puts("No");
	
	return 0;
}

K - Class

題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=6577

題目大意:x,y分別是a,b的和,差,求a*b

解題思路:求出a,b;相乘即可

#include<iostream>

using namespace std;

int main(){
	int x,y;
	cin>>x>>y;
	int a,b;
	a=(x+y)/2;
	b=x-a;
	cout<<a*b<<endl;
	
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章