Codeforces Round #305 (Div. 1) A B C

作者在比赛中遇到困难,认识到自己的不足之处,决定专注于一个账号,通过实战提高实力。文章涉及了两个具体问题:一个关于线性同余方程的解决方法,另一个关于连续数列中最小最大值的查找。此外,作者分享了使用并查集解决问题的方法,并介绍了容斥原理在计算最大公约数为1的启用水量对数量中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        这场比赛爆零了,感觉自己还是弱。。而且有点过于care rating了。做比赛还开小号,不敢用大号。根本没有攻克难题的魄力和勇气。

        以后只打一个号就好了,实力增强了总是能涨的。这场的题应该还是偏简单的,比赛的时候还是各种2各种想不出,感谢round305暴露了我很多弱点。


Mike and Frog

        每过一秒,h1=(x1*h1+y1)%m,h2=(x2*h2+y2)%m。求使h1=a1,h2=a2同时满足的最早时刻。

        找到使h1第一次等于a1的时间,再找使它循环的时间,对h2同理。然后弄到set里面乱搞就好了。更科学的做法应该是扩展欧几里德。

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

#define ll long long
const int INF = 1e9;

int main(){
	ll m,h1,a1,x1,y1;
	ll   h2,a2,x2,y2;
	cin>>m;
	cin>>h1>>a1;
	cin>>x1>>y1;
	cin>>h2>>a2;
	cin>>x2>>y2;
	//
	ll ans1=1;
	for(;ans1<=m+1;ans1++){
		h1=x1*h1+y1;
		h1%=m;
		if(h1==a1){
			break;
		}
	}
	
	ll ans2=1;
	for(;ans2<=m+1;ans2++){
		h2=x2*h2+y2;
		h2%=m;
		if(h2==a2){
			break;
		}
	}
	
	ll cyc1=1;
	for(;cyc1<=m+1;cyc1++){
		h1=x1*h1+y1;
		h1%=m;
		if(h1==a1){
			break;
		}
	}
	
	ll cyc2=1;
	for(;cyc2<=m+1;cyc2++){
		h2=x2*h2+y2;
		h2%=m;
		if(h2==a2){
			break;
		}
	}
	
	bool ok=1;
	if(ans1>m+1||ans2>m+1)ok=0;
	
	if(ok){
		if(cyc1>m+1)cyc1=0;
		if(cyc2>m+1)cyc2=0;

		set<ll> s;
		for(int i=0;i<=m;i++){
			s.insert(ans1+cyc1*i);
		}
		for(int i=0;i<=m;i++){
			if(s.count(ans2+cyc2*i)){
				cout<<ans2+cyc2*i<<endl;
				return 0;
			}
		}
		
		cout<<-1;
	}else{
		cout<<-1;
	}
	return 0;
}      


Mike and Feet

        长度为n的数列,求连续长度x=1~n的一串数中,最小值的最大值。

        维护一个单调栈,使值递增。出栈时维护长度并尝试更新答案,具体见代码。

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

#define ll long long
const int INF = 1e9;

struct node{
	int val;
	int len;
	node(int val,int len):val(val),len(len){
	}
	node(){
	}
};
stack<node> sta;

int a[200010];
int ans[200010];

int main(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		int curlen=0;
		while(sta.size() && sta.top().val>=a[i]){
			int val=sta.top().val;
			curlen+=sta.top().len;
			sta.pop();
			ans[curlen]=max(ans[curlen],val);
		}
		sta.push(node(a[i],curlen+1));
	}
	
	int curlen=0;
	while(sta.size()){
		int val=sta.top().val;
		curlen+=sta.top().len;
		sta.pop();
		ans[curlen]=max(ans[curlen],val);
	}
	
	for(int i=n;i>=1;i--){
		if(ans[i]<ans[i+1])ans[i]=ans[i+1];
	}
	for(int i=1;i<=n;i++){
		printf("%d ",ans[i]);
		
	}cout<<endl;
	return 0;
} 

        另外我还看到了一个使用并查集的做法。先排序,从大到小标记元素,并查集维护当前元素与左右已被标记的元素连起来的最大长度。


Mike and Foam

        有n个数,初始均为禁用状态。有q个询问,每次改变一个数的状态(禁用<-->启用)。问启用的那些数里面,有多少对数满足最大公约数为1。

        这是我第一道用容斥原理做的题。。本来没想法,看了题解后发现非常精妙。首先把1~500000的所有素因子筛出来(注意到每个数最多有6个不同的素因子)。假设新增或删除的数与其他启用的数最大公约数都等于1,那么新增或删除后,答案的改变量就是除了它之外,启用的数的个数(新增加上,删除减去)。但是,新增或删除的那个数,可能与某些数的最大公约数不为1,这就需要用容斥原理来处理。比如ai共有6个素因子,那么就用6位二进制数把它的素因子的所有子集表示出来,为所有素因子集合计数,维护答案。

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

#define ll long long
const int INF = 1e9;

const int maxn =200010;
const int maxa =500000;

int a[maxn];
bool state[maxn];

vector<int> factors[maxa+1];
int cnt[maxa+1];	//统计素因子的集合 

void init(){
	for(int i=2;i<=500000;i++){
		if(!factors[i].size()){
			for(int j=i;j<=500000;j+=i){
				factors[j].push_back(i);
			}
		}
	}
}

static ll ans=0;
static int tot=0;

void fun(int num,int flag){
	int t=1<<factors[num].size();
	ans+=tot*flag;
	//枚举子集 
	for(int i=1;i<t;i++){
		int tmp=1;	//num素因子的子集中,所有元素的积。 
		int bit=0;	//num素因子的子集中,有多少个元素。
		for(int j=0;j<factors[num].size();j++){
			if(i&(1<<j)){
				tmp*=factors[num][j];
				bit++;
			}
		}
		
		if(flag==-1)cnt[tmp]+=flag;
		
		int one;
		if(bit&1)one=1;
		else one=-1;		
		if(cnt[tmp]){
			//容斥维护答案,加多了减去,减多了加上。。。 
			ans-=cnt[tmp]*flag*one;
		}
		
		if(flag==1)cnt[tmp]+=flag;
	}
}

int main(){
	init();
	
	int n,q;
	cin>>n>>q;
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
	}
	int x;
	for(int i=1;i<=q;i++){
		scanf("%d",&x);
		if(state[x]){
			tot--;
			state[x]=0;
			fun(a[x],-1);
		}else{
			state[x]=1;
			fun(a[x],1);
			tot++;
		}
		printf("%I64d\n",ans);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值