【超好懂的比赛题解】2021CCPC哈尔滨站 个人题解

57 篇文章 0 订阅
12 篇文章 0 订阅

title : 2021CCPC哈尔滨站 题解
date : 2022-10-4
tags : ACM,题解,练习记录
author : Linno


2021CCPC哈尔滨站 题解

题目链接:https://codeforces.com/gym/103447

补题进度:7/12

B-Magical Subsequence

A的值很小,我们直接枚举两个数的和,然后遍历一遍看看能凑多少对,记录最长的答案即可。map应该会超时,建议用前缀和。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+7;

int n,a[N],sum[205][N];

signed main(){
	scanf("%d",&n);
	int ans=0;
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
		for(int j=1;j<=100;++j) sum[j][i]=sum[j][i-1];
		++sum[a[i]][i];
	}
	for(int s=2;s<=200;++s){ //枚举和 
		int len=0,lst=0;
		for(int i=2;i<=n;++i){
			if(s-a[i]<0||s-a[i]>100) continue;
			if(sum[s-a[i]][i-1]-sum[s-a[i]][lst]){
				len+=2;
				lst=i;
			}
		}
		if(len>ans) ans=len;
	}
	printf("%d\n",ans);
	return 0;
}

C-Colorful Tree

滚回去重学DSU on Tree了。

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+7;

int n,col[N],dp[N],fa[N];
vector<int>G[N];
multiset<int>st[N]; 

void dfs(int x){
	if(!G[x].size()){ //叶子结点,可以减少的次数为0 
		st[x].insert(col[x]);
		dp[x]=1; 
		return;
	}
	int mx=1;
	for(auto to:G[x]){
		if(to==fa[x]) continue;
		dfs(to); //算出儿子结点的答案 
		dp[x]+=dp[to]; //
		if(st[x].size()<st[to].size()) swap(st[to],st[x]);
		for(auto it:st[to]){  //小的集合向大的集合合并 
			st[x].insert(it);
			mx=max(mx,(int)st[x].count(it)); //记录出现最多的颜色次数 
		}
	}
	if(mx>1){  
		multiset<int>ss;
		for(auto it:st[x]){  //合并 
			if(!ss.count(it)&&st[x].count(it)==mx) ss.insert(it);
		}
		swap(st[x],ss);
	}
	//总和-儿子需要的颜色次数+这个结点染色可以减少maxn次染色
	dp[x]=dp[x]-G[x].size()+mx; 
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n;
	for(int i=2,x;i<=n;++i){ //建树 
		cin>>fa[x];
		G[fa[x]].emplace_back(i);
	}
	int ans=0;
	for(int i=1;i<=n;++i){
		cin>>col[i]; 
		if(col[i]) ++ans; 
	}
	dfs(1);
	cout<<ans-dp[1]+1; //总数-可以减少的+第一个节点染色 
	return 0;
}

D-Math master

显然我们可以用 O ( 2 k ) O(2^k) O(2k)去枚举k位数p的约分结果a,那么通过等式 p q = a b \frac{p}{q}=\frac{a}{b} qp=ba能够求出b,只需要用 O ( k ) O(k) O(k)去检查b是否可行即可,可行的条件是a删去的数集刚好是b删去的数集,且其他数跟q一样。其他的特判大概就是当 a = = 0 a==0 a==0以及 a ∗ q % p ! = 0 a*q\%p!=0 aq%p!=0

#include<bits/stdc++.h>
#define int __int128
using namespace std;

int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}
void write(int x){if(x>9) write(x/10ll);putchar(x%10+'0');}

int n,p,q,vis[10],tmp[10],lenq,lenp,ans1,ans2;
char sp[25],sq[25];

inline int gcd(int a,int b){
	return b?gcd(b,a%b):a;
}

bool check(int x){
	for(int i=lenq-1;i>=0;--i){
		if(sq[i]-'0'==x%10){
			x/=10;
			continue;
		}
		++tmp[sq[i]-'0'];
	}
	if(x) return 0;
	for(int i=0;i<=9;++i) if(tmp[i]!=vis[i]) return 0;
	return 1;
}

signed main(){
	n=read();
	for(int i=1;i<=n;++i){
		p=0;q=0;
		scanf("%s%s",sp,sq);
		lenp=strlen(sp),lenq=strlen(sq);
		for(int i=0;i<lenp;++i) p=p*10ll+(sp[i]-'0');
		for(int i=0;i<lenq;++i) q=q*10ll+(sq[i]-'0');
		ans1=p;ans2=q;
		for(int i=0;i<(1<<(lenp));++i){
			int a=0,b;
			for(int j=0;j<=9;++j) vis[j]=0,tmp[j]=0;
			for(int j=0;j<lenp;++j){
				if((i>>j)&1) a=a*10ll+(sp[j]-'0'); 
				else vis[sp[j]-'0']++;  //记录删去的数 
			}
			if(!a||a*q%p) continue;
			b=a*q/p;
			if(check(b)){
				ans1=min(ans1,a);
				ans2=min(ans2,b);
			}
		}
		write(ans1);putchar(' ');
		write(ans2);putchar('\n');
	}
	return 0;
}

E-Power and Modulo

显然当第一次 A i ≠ 2 i − 1 m o d    M A_i \neq 2^{i-1} \mod M Ai=2i1modM时,M就已经确定了,而这个时候2的幂次显然是不会大于 2 e 9 2e9 2e9的,这个时候再对前面和后面的数取模代入等式检查就可以了。(简而言之暴力)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+7;

int t,n,a[N];

void solve(){
	cin>>n;
	int no=0,ans=0;
	for(int i=1;i<=n;++i) cin>>a[i];
	for(int i=1,st=1;i<=n;++i){
		if(ans){
			if(a[i]==st%ans){
				st=st*2ll%ans;
				continue;
			}else{
				no=1;
				break;
			}
		}else{
			if(a[i]==st){st=st*2ll;continue;}
			else{
				ans=(st-a[i]);
				if(ans<=0){
					no=1;
					break;
				}
				for(int j=1;j<i;++j) if(a[j]!=(1ll<<(j-1))%ans) no=1;
				if(no)	break;
				st=st*2ll%ans;
			}
		}
	}
	if(no||!ans) cout<<"-1\n";
	else cout<<ans<<"\n";
} 

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

G-Damaged Bicycle

首先我们可以预处理出k+1个关键点(包括1)开始的最短路。这个数据范围可以想到状压, d p [ s t ] [ x ] dp[st][x] dp[st][x]表示从第 x x x个关键点出发,经过的关键点集合为 s t st st时的最短期望到达时间,对于每一个 < s t , x > <st,x> <st,x>,策略都是固定的,所以可以记忆化搜索。从当前集合出发,我们很容易得到这个点不经过其他关键点时到达终点的期望时间(就是算一下拿到车和拿不到时去n号点要多久),而且对于从他出发到达任意一个关键点(仅当这个点的车坏掉)的时间是 p [ x ] ∗ ( 1.0 ∗ d i s [ x ] [ a [ i ] ] / t p[x]*(1.0*dis[x][a[i]]/t p[x](1.0dis[x][a[i]]/t,最后一部分加上那个关键点的到终点的最小期望时间即可(即转移到 d p [ s t ∣ ( 1 < < ( p − 1 ) ) ] [ p ] dp[st|(1<<(p-1))][p] dp[st(1<<(p1))][p])。

#include<bits/stdc++.h>
#define mk make_pair
#define pii pair<int,int>
#define S second
#define F first
#define inf 0x3f3f3f3f
#define int long long 
using namespace std;
const int N=1e5+7;

struct E{
	int v,w,nxt;
}e[N<<2];

int head[N],cnt=0;
inline void addedge(int u,int v,int w){
	e[++cnt]=(E){v,w,head[u]};head[u]=cnt;
}

int n,m,k,a[20];
double t,r,p[20],dp[1<<20][20];
int dis[20][N],vis[N];

void dij(int s,int id){
	for(int i=1;i<=n;++i) dis[id][i]=inf,vis[i]=0;
	dis[id][s]=0;
	priority_queue<pii>q;
	q.emplace(mk(0,s));
	while(q.size()){
		int fro=q.top().S;
		q.pop();
		if(vis[fro]) continue;
		vis[fro]=1;
		for(int i=head[fro];i;i=e[i].nxt){
			int to=e[i].v,w=e[i].w;
			if(dis[id][to]>dis[id][fro]+w){
				dis[id][to]=dis[id][fro]+w;
				q.emplace(mk(-dis[id][to],to));
			}
		}
	}
}

double dfs(int st,int x){ //得到从p[x]点出发且好的单车集合为st时的期望时间 
	if(dp[st][x]) return dp[st][x]; //记忆化 
	double res=1.0*p[x]*dis[x][n]/t+(1.0-p[x])*dis[x][n]/r;
	for(int i=1;i<=k;++i){  //该点单车坏掉,选一个未走过的点,从其期望最短时间转移 
		if(st&(1ll<<(i-1))) continue;
		res=min(res,1.0*(1-p[x])*dis[x][n]/r+p[x]*(1.0*dis[x][a[i]]/t+dfs(st|(1<<(i-1)),i)));
	}
	return dp[st][x]=res;
}

signed main(){
	scanf("%lf%lf",&t,&r);
	scanf("%lld%lld",&n,&m);
	for(int i=1,u,v,w;i<=m;++i){
		scanf("%lld%lld%lld",&u,&v,&w);
		addedge(u,v,w);
		addedge(v,u,w);
	}
	scanf("%lld",&k);
	for(int i=1;i<=k;++i){
		scanf("%lld%lf",&a[i],&p[i]);
		p[i]/=100.0;
	}
	a[0]=1;p[0]=1.0;
	for(int i=0;i<=k;++i) dij(a[i],i); //求k+1次最短路 
	if(dis[0][n]>=inf){ //-1的情况是不存在联通 
		printf("-1\n");
		return 0;
	}
	printf("%.10lf\n",dfs(0,0)); //开始状压dp 
	return 0;
}

I-Power and Zero

想象一下二进制下第i位的1可以看作是2个第i-1位的1,因此我们肯定是可以重新分配每一个二进制数1的个数使得 c n t [ i ] < = c n t [ i − 1 ] cnt[i]<=cnt[i-1] cnt[i]<=cnt[i1]的,最终我们可以得到一个阶梯型的序列,就能直观地算出答案是尽量取1。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+7;

int t,n,a[N],cnt[55];

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>t;
	while(t--){
		cin>>n;
		for(int i=0;i<=40;++i) cnt[i]=0;
		for(int i=1;i<=n;++i){
			cin>>a[i];
			for(int j=0;j<=40;++j){
				if(a[i]&(1ll<<j)){
					++cnt[j];
				}
			}
		}
		while(1){
			int flag=1;
			for(int i=40;i>=1;--i){
				if(cnt[i]>cnt[i-1]){ 
					flag=0;
					int tmp=(cnt[i]*2+cnt[i-1])/3; //分成1:2需要的操作数 
					cnt[i-1]+=(cnt[i]-tmp)*2;
					cnt[i]=tmp;
				}
			}
			if(flag) break; 
		}
		cout<<cnt[0]<<"\n";
	}
	return 0;
}

J-Local Minimum

模拟签到题。会英文直接做就行。

#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=1e5+7;

int n,m,mp[1005][1005],minx[1005],miny[1005],ans=0;

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n>>m;
	memset(minx,0x3f,sizeof minx);
	memset(miny,0x3f,sizeof miny);
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			cin>>mp[i][j];
			if(mp[i][j]<minx[i]) minx[i]=mp[i][j];
			if(mp[i][j]<miny[j]) miny[j]=mp[i][j];
		}
	}
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
		//	cout<<i<<" "<<j<<" "<<minx[i]<<" "<<miny[j]<<"!!\n";
			if(mp[i][j]==minx[i]&&mp[i][j]==miny[j]) ++ans;
		} 
	}
	cout<<ans<<"\n";
	return 0;
} 
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RWLinno

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值