Codeforces Round 988 (Div. 3) 个人题解 (A~G)

Codeforces Round 988 (Div. 3) 个人题解 (A~G)

Codeforces Round 988 (Div. 3)

Problem - A - Codeforces

解题思路

  • 设每一种数有x个,则该数贡献为x/2 向下取整
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
int a[30];
void solve(){
	int n;cin>>n;
	map<int,int> mp;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		mp[a[i]]++;
	}
	int ans=0;
	for(auto [x,v]:mp){
		ans+=v/2;
	}
	cout<<ans<<endl;
}
int main(){
	std::ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int T;cin>>T;
	while(T--) solve();
	return 0;
}

Problem - B - Codeforces

解题思路

  • 去掉行,列的输入则还剩下k-2个值,直接暴力枚举(k-2)的因数,判断有没有出现过即可
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
const int N=2e5+10;
int a[N];
void solve(){
	int k;cin>>k;
	map<int,int> mp;
	for(int i=1;i<=k;i++){
		cin>>a[i];
		mp[a[i]]=1;
	}
	int t=k-2;
	for(int i=1;i<=t;i++){
		if(t%i) continue;
		int n=i,m=t/i;
		if(mp[n] && mp[m]){
			cout<<n<<" "<<m<<endl;
			return;
		}
	}
}
int main(){
	std::ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int T;cin>>T;
	while(T--) solve();
	return 0;
}

Problem - C - Codeforces

解题思路

  • 构造题,首先想到,n=1时答案为1,1<=n<=4时无解,n>=5时考虑构造
  • 构造序列3,1,5,4,2,这是n=5时的答案,接下来只需奇数往前插入,偶数往后插入,(这样的话新产生的和一定为偶数且大于2),这样构造即可
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
void solve(){
	int n;cin>>n;
	if(n==1){
		cout<<1<<endl;
		return;
	}
	if(n<=4){
		cout<<-1<<endl;
		return;
	}
	deque<int> q;
	q.push_back(3);
	q.push_back(1);
	q.push_back(5);
	q.push_back(4);
	q.push_back(2);
	for(int i=6;i<=n;i++){
		if(i&1) q.push_front(i);
		else q.push_back(i);
	}
	for(auto x:q) cout<<x<<" ";
	cout<<endl;
}
int main(){
	std::ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int T;cin>>T;
	while(T--) solve();
	return 0;
}

Problem - D - Codeforces

解题思路

  • 感觉是模拟题?
  • 遇到一个障碍跳不过去的时候,考虑要去踩装置获得能量,那只需要将能量加到能够越过障碍即可,所以考虑踩当前能踩到的加最大能量的装置,用一个大根堆维护一下,枚举障碍即可
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=2e5+10;
int ll[N],r[N],xx[N],v[N];
void solve(){
	int n,m,l;cin>>n>>m>>l;
	for(int i=1;i<=n;i++) cin>>ll[i]>>r[i];
	for(int i=1;i<=m;i++) cin>>xx[i]>>v[i];
	int idx=1,ans=0;
	int k=1;
	priority_queue<int> q;
	for(int i=1;i<=n;i++){
		int len=r[i]-ll[i]+2;
		while(idx<=m && xx[idx]<ll[i]){
			q.push(v[idx]);
			idx++;
		}
		while(q.size() && k<len){
			int t=q.top();
			q.pop();
			ans++;
			k+=t;
		}
		if(k<len){
			cout<<-1<<endl;
			return;
		}
	}
	cout<<ans<<endl;
}
signed main(){
	std::ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int T;cin>>T;
	while(T--) solve();
	return 0;
}

Problem - E - Codeforces

解题思路

  • 交互题,太久没做了快忘记怎么做了
  • 考虑每次询问(1,k),设每次询问的结果为a[i],若a[i]=a[i-1],则ans[i]=0,若a[i]>a[i-1],则ans[i]=1,(这是在前面已经出现0的情况下)
  • 那如何判断前导的1呢?若为11000111,那么在前面的询问中a[i]=a[i-1],我们只需要找到第一个a[i]!=0位置id.a[id]表示前id-1个数里有a[id]个0,那么最左端则有id-1-a[id]个1
#include<bits/stdc++.h>
using namespace std;
const int N=1e4+10;
int a[N],ans[N];
int ask(int x){
	cout<<"? "<<1<<" "<<x<<endl;
	int res;cin>>res;
	return res;
}
void solve(){
	int n;cin>>n;
	for(int i=1;i<=n;i++) ans[i]=0;
	for(int i=2;i<=n;i++) a[i]=ask(i);
	if(a[n]==0){
		cout<<"! IMPOSSIBLE"<<endl;
		return;
	}
	for(int i=2;i<=n;i++){
		if(a[i]>a[i-1]) ans[i]=1;
	}
	for(int i=2;i<=n;i++){
		if(ans[i]==1){
			int tmp=a[i];
			for(int j=1;j<=i-1-a[i];j++) ans[j]=1;
		}
	}
	cout<<"! ";
	for(int i=1;i<=n;i++) cout<<ans[i];
	cout<<endl;
}
int main(){
	std::ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int T;cin>>T;
	while(T--) solve();
	return 0;
}

Problem - F - Codeforces

题目分析

  • 二分答案,我们考虑对于一个地方进攻cnt次,那么每个地方每次收到的伤害至少为\(\lceil h[i]/cnt \rceil\),假设该值为t,那么进攻范围则应该在[x[i]-m+t,x[i]+m-t],我们队每个敌人都处理出这样一个区间,接下来比较经典,查询是否有被覆盖超过k次的点,可以差分加离散化处理,也可以直接差分累加判断
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
const int N=1e5+10;
using namespace std;
int n,m,k;
int h[N],x[N];
typedef pair<int,int> PII; 
bool check(int cnt){
	vector<PII> v;
	for(int i=1;i<=n;i++){
		if(m*cnt<h[i]) continue;
		int t=(h[i]+cnt-1)/cnt;
		int l=x[i]-m+t;
		int r=x[i]+m-t;
		v.push_back({l,1});
		v.push_back({r+1,-1});
	}
	sort(v.begin(),v.end());	
	int res=0;
	for(auto [s,y]:v){
		res+=y;
		if(res>=k) return true;
	}
	return false;
}
void solve(){
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++) cin>>h[i];
	for(int i=1;i<=n;i++) cin>>x[i];
	int ans=-1;
	int l=1,r=1e9;
	while(l<=r){
		int mid=(l+r)>>1;
		if(check(mid)){
			r=mid-1;
			ans=mid;
		}
		else l=mid+1;
	}
	cout<<ans<<endl;
}
signed main(){
	std::ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int T;cin>>T;
	while(T--) solve();
	return 0;
}

Problem - G - Codeforces

题目分析

  • 考虑O(n2)朴素dp,f[i]表示到达i点时的方案总数,则f[i]=\(\sum_{j=1}^{i-1} f_j\) (gcd(a[i],a[j])!=1),这样就是n方的dp,考虑优化
  • gcd(a[i],a[j]!=1)实际上就是两数至少有一个相同的质因子,我们可以这样考虑,假设a[i]分解出的质数有2,3,5,那么f[i]需要加上的则为2,3,5的所有组合,这里可以状压处理,但是这中间算重合的部分如何处理?考虑容斥计算
  • 我们记录sum[i]为前面中所有含有i因子的状态总和,则f[i]=sum[2]+sum[3]+sum[5]-sum[6]-sum[10]-sum[15]+sum[30],即容斥的去算
  • 处理每个数的质因子的部分可以先预处理出每个数的最小质因子,这样能够加速预处理
#include<bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
const int N=1e6+10;
const int MAXN=1e6;
const int mod=998244353;
int tot=0;
int a[N],pri[N],vis[N],mn[N],f[N],sum[N];
vector<int> g[N];
void init(){
	for(int i=2;i<=MAXN;i++){
		if(!vis[i]){
			pri[++tot]=i;
			mn[i]=i;
		}
		for(int j=1;j<=tot && pri[j]*i<=MAXN;j++){
			int num=pri[j]*i;
			vis[num]=1;
			mn[num]=pri[j];
			if(i%pri[j]==0) break;
		}
	}
}
void solve(){
	init();
	int n;cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++){
		int tmp=a[i];
		while(tmp!=1){
			int v=mn[tmp];
			g[i].push_back(v);
			while(tmp%v==0) tmp/=v;
		}
	}
	f[1]=1;
	for(int i=1;i<=n;i++){
		vector<int> v;
		int cnt=g[i].size();
		for(int j=1;j<(1ll<<cnt);j++){
			int mul=1,p=-1;
			for(int k=0;k<cnt;k++){
				if((j>>k)&1){
					mul*=g[i][k];
					p*=-1;
				}
			}
			v.push_back(mul);
			f[i]+=p*sum[mul]%mod;
			f[i]%=mod;	
		}
		for(auto x:v){
			sum[x]+=f[i];
			sum[x]%=mod;
		}
	}
	int ans=(f[n]+mod)%mod;
	cout<<ans<<endl;
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	int T=1;
	while(T--) solve();
	return 0;
}

posted @ 2024-11-21 15:08  Persona_owl  阅读(3)  评论(0编辑  收藏  举报