湖大训练赛2 Modified LCS

解这道题目之前,我们先来回顾一下我们的老题:青蛙的约会。http://poj.org/problem?id=1061

有两只青蛙A、B,他们在一条线上同向而行,A在X位置,每次跳M格,B在Y位置,每次跳N格。这条线有L长,首尾相连。为最少跳多少次他们会相遇在同一点。或者永远不能相见。Impossible。这就像我博客上的签名档:彼此追逐却有着永恒的距离。

我们可以根据这些条件列出一个方程出来:(X+M*t)%L=(Y+N*t)%L其中t代表跳了多少次。由同余定理可知:X+M*t=Y+N*t+k*L。这个k可正可负。由此可变化成我们熟悉的二元一次方程,运用扩展欧几里得求得解。

(N-M)*t+k*L=Y-X(其中只有t和k是未知数)即ax+by=gcd(a,b)。我们可以把a=N-M,b=Y-X.这样就变成a*t+k*L=b。根据扩展欧几里得可以求得a和k的值。但是这个a,k不是最小的整数解。这个方程最小整数解的条件是b % gcd(l,a)等于0.因为a,k分别得除以gcd(l,a)再乘上b,对应的才是方程式a*t+k*L=b的解。假设我们有最小整数解,我们怎么去解呢?我们设r=b / gcd(l,a).最小整数解应该是:t(min)=(t/gcd(l,a)*b % r+r)r.kmin=(b-a(min)*t)/L;好吧,我自己也有点说不清楚了,得自己用笔化化化。总之就是这样了。不懂的留言或者私聊。

(x1,y1)=(x0+b/gcd(a,b),y0-a/gcd(a,b))

#include <iostream>
#include <cstring>
using namespace std;
#define LL long long
LL x,y,gcd;
void exgcd(LL a,LL b)
{
	if (b==0)
	{
		x=1;
		y=1;
		gcd=a;
		return;
	}
	exgcd(b,a%b);
	LL t=x;
	x=y;
	y=t-a/b*y;
	return;
}
int main()
{
	LL x1,y1,m,n,l;
	while (cin>>x1>>y1>>m>>n>>l)
	{
		LL a=n-m;
		LL b=x1-y1;
		exgcd(l,a);
		LL r=l/gcd;
		if (b % gcd) cout<<"Impossible"<<endl;
		else 
		cout<<(y*b/gcd % r+r) % r<<endl;
	}
	return 0;
}

Modified LCS
Time Limit: 10000ms, Special Time Limit:25000ms, Memory Limit:65536KB
Total submit users: 32, Accepted users: 21
Problem 12831 : No special judgement
Problem description

LCS stands for longest common subsequence, and it is a well known problem. A sequence in this problem means a list of integers, and a sequence X is considered a subsequence of another sequence Y, when the sequence X can be obtained by deleting zero or more elements from the sequence Y without changing the order of the remaining elements.
In this problem you are given two sequences and your task is to find the length of the longest sequence which is a subsequence of both the given sequences.
You are not given the sequences themselves. For each sequence you are given three integers N, F and D, where N is the length of the sequence, F is the first element in the sequence. Each element except the first element is greater than the element before it by D.
For example N = 5, F = 3 and D = 4 represents the following sequence: [3, 7, 11, 15, 19].
There will be at least one integer which belongs to both sequences and it is not greater than 1,000,000.

Input

Your program will be tested on one or more test cases. The first line of the input will be a single integer T, the number of test cases T ranges in(1,100). Followed by the test cases, each test case is described in one line which contains 6 integers separated by a single space N1 F1 D1 N2 F2 D2 (N1,N2 ranges in(1,10^18) and F1,D1,F2,D2 ranges in(1,10^9)) representing the length of the first sequence, the first element in the first sequence, the incremental value of the first sequence, the length of the second sequence, the first element in the second sequence and the incremental value of the second sequence, respectively.

Output

For each test case, print a single line which contains a single integer representing the length of the longest common subsequence between the given two sequences.

Sample Input
3
5 3 4 15 3 1
10 2 2 7 3 3
100 1 1 100 1 2
Sample Output
4
3
50
Problem Source
ACPC 2013
然后我们再回到这个题目来。题意应该容易懂,是不是发现和上面青蛙的约会有点类似。也是需要解一个二元一次方程解然后求得最小整数解。我先把式子列出来。F1+X*D1=F2+Y*D2.平移之后会发现这里是减去D2,所以我们代入exgcd的时候是-D2。然后会求得一组(X,Y),同理,我们设F=F1-F2. R=F/gcd(最大公约数)。然后我们的最小整数解就应该是Xmin=(X/gcd*F%R+R)%R.Ymin=(F-Xmin*d1)/(-d2)。这里慢慢体会下。然后我们怎么求得他们相遇的点数呢?我们可以这样做,他们分别不是有n1,n2项吗?然后他们的第一个相遇起始点分别就是Xmin,Ymin。只要知道他们两个范围最小的Min((n1-X-1)/dx.(n2-Y-1)/dy)+1即可。这里dx=abs(d2/gcd),dy=abs(d1/gcd).嗯,当然前提得要判断有木有整数解。就是这样。Gg。感谢Darkdream陪我一起巩固了扩展GCD的知识。加油!

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
#include <queue>
#include <map>
#include <stack>
#include <list>
#include <vector>
using namespace std;
#define LL __int64
LL x,y,gcd;
void exgcd(LL a ,LL b)
{
	if (!b)
	{
		x=1;
		y=1;
		gcd=a;
		return;
	}
	exgcd(b,a%b);
	LL t=x;
	x=y;
	y=t-a/b*y;
	return ;
}
int main()
{
	int T;
	LL n1,n2,f1,f2,d1,d2;
//	freopen("in.txt", "r", stdin);
//	freopen("outt.txt","w",stdout);
	scanf("%d",&T);
	while (T--)
	{
		cin>>n1>>f1>>d1>>n2>>f2>>d2;
		LL f=f2-f1;
		exgcd(d1,-d2);
		if (f % gcd)
		{
			puts("0");
			continue;
		}
		else
		{
			LL r=abs((-d2)/gcd);
			x=(x*f/gcd % r+r)%r;
			y=(f-x*d1)/(-d2);
			LL dx=abs(d1/gcd);
			LL dy=abs(d2/gcd);
			LL ans=min((n1-x-1)/dy,(n2-y-1)/dx)+1;
			cout<<ans<<endl;
		}
	} 
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值