Codeforces 338D GCD Table 中国剩余定理

74 篇文章 0 订阅
12 篇文章 0 订阅

题目链接:点击打开链接


给定n*m的矩阵,[i,j]的点值为gcd(i,j)

给定一个k长的序列,问是否能匹配上 矩阵的某一行的连续k个元素

思路:

我们要求出一个解(i,j) 使得 i<=n && j<=m 此时输出 YES

对于j

j % b[0] = 0

j+1 % b[1] = 0

···

j+l % b[l] = 0

根据定理:若 a == b (mod n) => (a+c) == b+c (mod n)

所以将上式变换为

j % b[0] = 0

j % b[1] = -1

···

j % b[n-1] = - (n-1)

最后求出的i,j 检验一下是否满足 gcd(i,j+k) == input[k];

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<math.h>
#include<set>
#include<queue>
#include<vector>
#include<map>
using namespace std;
#define ll __int64
ll gcd(ll a, ll b) {
	return b == 0 ? a : gcd(b, a%b);
}
void extend_gcd (ll a , ll b , ll& d, ll &x , ll &y) {  
	if(!b){d = a; x = 1; y = 0;}
	else {extend_gcd(b, a%b, d, y, x); y-=x*(a/b);}
}
ll china(ll l, ll r, ll *m, ll *a){ //下标[l,r] 方程x%m=a;
	ll lcm = 1;
	for(ll i = l; i <= r; i++)lcm = lcm/gcd(lcm,m[i])*m[i];
	for(ll i = l+1; i <= r; i++) {
		ll A = m[l], B = m[i], d, x, y, c = a[i]-a[l];
		extend_gcd(A,B,d,x,y);
		if(c%d)return -1;
		ll mod = m[i]/d;
		ll K = ((x*c/d)%mod+mod)%mod;
		a[l] = m[l]*K + a[l];
		m[l] = m[l]*m[i]/d;
	}
	if(a[l]==0)return lcm;
	return a[l];
}
#define N 10005
ll n[N],b[N],tmp[N],len,nn,mm;
bool work(){
	memset(b, 0, sizeof b);
	memcpy(tmp,n,sizeof n);
	ll i = china(1,len,n,b);
	if(i>nn || i<=0)return false;
	for(ll hehe = 1; hehe <= len; hehe++)b[hehe] = -hehe+1;
	memcpy(n,tmp, sizeof n);
	ll j = china(1,len,tmp,b);
	if(j+len-1>mm || j<=0)return false;
	for(ll hehe = 1; hehe <= len; hehe++)if(gcd(i,j+hehe-1)!=n[hehe])return false;
	return true;
}
int main(){  
	while(cin>>nn>>mm>>len){
		for(ll i = 1; i <= len; i++) cin>>n[i];
		work()?puts("YES"):puts("NO");
	}
	return 0;  
}  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CodeForces - 616D是一个关于找到一个序中最长的第k好子段的起始位置和结束位置的问题。给定一个长度为n的序和一个整数k,需要找到一个子段,该子段中不超过k个不同的数字。题目要求输出这个序最长的第k好子段的起始位置和终止位置。 解决这个问题的方法有两种。第一种方法是使用尺取算法,通过维护一个滑动窗口来记录\[l,r\]中不同数的个数。每次如果这个数小于k,就将r向右移动一位;如果已经大于k,则将l向右移动一位,直到个数不大于k。每次更新完r之后,判断r-l+1是否比已有答案更优来更新答案。这种方法的时间复杂度为O(n)。 第二种方法是使用枚举r和双指针的方法。通过维护一个最小的l,满足\[l,r\]最多只有k种数。使用一个map来判断数的种类。遍历序,如果当前数字在map中不存在,则将种类数sum加一;如果sum大于k,则将l向右移动一位,直到sum不大于k。每次更新完r之后,判断i-l+1是否大于等于y-x+1来更新答案。这种方法的时间复杂度为O(n)。 以上是两种解决CodeForces - 616D问题的方法。具体的代码实现可以参考引用\[1\]和引用\[2\]中的代码。 #### 引用[.reference_title] - *1* [CodeForces 616 D. Longest k-Good Segment(尺取)](https://blog.csdn.net/V5ZSQ/article/details/50750827)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Codeforces616 D. Longest k-Good Segment(双指针+map)](https://blog.csdn.net/weixin_44178736/article/details/114328999)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值