UVA 11754 (暴力+中国剩余定理)

题目链接http://www.bnuoj.com/v3/problem_show.php?pid=20172

题目大意:有C个模方程,每个方程可能有k余数,求最小的S个解。

解题思路

看见模方程就想到中国剩余定理,然后看下确定的方程情况。

由乘法原理,共有II ki 种情况,即求解II ki 次。k比较大时基本完蛋。

其实解模方程还有一种暴力方法,就是选定一个模方程,令t=0,1...., n=t*LCM+余数(n一定要大于0)

通过t不断增大这种迭代方式从小到大创造一些可能解n,然后去测试其它方程,看余数对不对。

如果余数全对,那么就找到了一个解。否则就砍掉。

因为测试是很快的,大部分数据一开始就被砍了,所以k比较大时速度非常快。

毕竟上面是看RP的暴力,所以设定一个分界(10000),如果II ki <10000 ,那么还是通过中国剩余定理来求解,复杂度O(n)。

方法就是DFS枚举出C个余数情况,然后求解。

由于求出的全是最小整数解,S比较大时,剩余定理的解可能不足,这时候从小到大每个值加M的倍数凑出更大的解。

#include "cstdio"
#include "set"
#include "vector"
#include "algorithm"
using namespace std;
#define LL long long
#define LIMIT 10000
int C,S,s;
LL m[15],k[15],y[15][105],a[15],M;
set<LL> value[15];
vector<LL> ans;
void solve_violence(int bc)
{
    for(int i=1;i<=C;i++)
    {
        value[i].clear();
        if(i!=bc) for(int j=1;j<=k[i];j++) value[i].insert(y[i][j]);
    }
   for(int t=0;S!=0;t++)
    {
        for(int i=1;i<=k[bc];i++)
        {
            LL n=t*m[bc]+y[bc][i];
            if(!n) continue;
            bool ok=true;
            for(int j=1;j<=C;j++)
            {
                if(j==bc) continue;
                if(!value[j].count(n%m[j])) {ok=false;break;}
            }
            if(ok) {printf("%lld\n",n);if(--S==0) return;}
        }
    }
}
LL ex_gcd(LL a,LL b,LL &x,LL &y)
{
    if(a==0&&b==0) return -1;
    if(b==0) {x=1;y=0;return a;}
    LL d=ex_gcd(b,a%b,y,x);
    y-=a/b*x;
    return d;
}
LL solve_china()
{
    LL res=0;M=1;
    for(int i=1;i<=C;i++) M*=m[i];
    for(int i=1;i<=C;i++)
    {
        LL w=M/m[i],x,y;
        ex_gcd(m[i],w,x,y);
        y=(y*w%M+M)%M;
        res=(res+y*a[i])%M;
    }
    return res;
}
void dfs(int dep)
{
    if(dep>C)
    {
        ans.push_back(solve_china());
        return;
    }
    for(int i=1;i<=k[dep];i++)
    {
        a[dep]=y[dep][i];
        dfs(dep+1);
    }
}
int main()
{
    //freopen("in.txt","r",stdin);
    while(scanf("%d%d",&C,&S)&&C)
    {
        int bestc=1;ans.clear();s=S;M=1;
        LL tot=1;
        for(int i=1;i<=C;i++)
        {
            scanf("%lld%lld",&m[i],&k[i]);
            tot*=k[i];
            if(k[i]*m[bestc]<k[bestc]*m[i]) bestc=i;
            for(int j=1;j<=k[i];j++)  scanf("%lld",&y[i][j]);
            sort(y[i]+1,y[i]+1+k[i]);
        }
        if(tot<LIMIT)
        {
            dfs(1);
            sort(ans.begin(),ans.end());
            for(int t=0;S!=0;t++)
                for(int i=0;i<ans.size();i++)
            {
                LL n=t*M+ans[i];
                if(n>0)
                {
                    printf("%lld\n",n);
                    if(--S==0) break;
                }
            }
        }
        else solve_violence(bestc);
        printf("\n");
    }
}
neopenx 445520 20172 Accepted GNU C++ 26 ms   2223 B 2015-02-06 23:10:03
时间: 2024-11-03 05:30:24

UVA 11754 (暴力+中国剩余定理)的相关文章

uva 756 - Biorhythms(中国剩余定理)

题目链接:uva 756 - Biorhythms 题目大意:三个周期,23,28,33,输入为分别为在新一年中(三个周期均从0开始),出现周期中峰值的一天,以及当前的日子,问说需要经过多少天,才能使得三个峰值的在同一天. 解题思路:裸的中国剩余定理. #include <cstdio> #include <cstring> typedef long long ll; const int maxn = 5; const ll m[maxn] = {23,28,33}; ll M,

uva 11754 - Code Feat(中国剩余定理+暴力)

题目链接:uva 11754 - Code Feat 题目大意:求一个数N,给出C和S,表示有C个条件,每个条件有X 和 k,然后是该个条件的k个yi,即NmodX=yj,输出满足的最小的S个N,要求正整数. 解题思路:total为所有的k的乘积,也就是可以作为一组完整限定条件的可能数,当个确定条件可以用中国剩余定理处理.但是如果total太大的话,处理的情况比较多.不过total数大的时候,可以通过枚举N来判断,找到一组k/x最小的最为枚举基准,然后判断即可. #include <cstdio

UVA 11754 Code Feat (枚举,中国剩余定理)

转载请注明出处: http://www.cnblogs.com/fraud/          ——by fraud C Code Feat   The government hackers at CTU (Counter-Terrorist Unit) have learned some things about the code, but they still haven't quite solved it.They know it's a single, strictly positive

UVa 11754 (中国剩余定理 枚举) Code Feat

如果直接枚举的话,枚举量为k1 * k2 *...* kc 根据枚举量的不同,有两种解法. 枚举量不是太大的话,比如不超过1e4,可以枚举每个集合中的余数Yi,然后用中国剩余定理求解.解的个数不够S个的时候,要把这些解分别加上M.2M...(M = X1 * X2 *...* Xc) 如果上述枚举量太大的话,直接枚举x.找一个k/X最小的条件,然后让x = t * X + Yi开始枚举,因为这样枚举x增长得最快,所以枚举次数也就最少.如果符合要求的话则输出. 上面两种方法都要注意所找到的解为正整

UVA 11754 - Code Feat(数论)

UVA 11754 - Code Feat 题目链接 题意:给定一个c个x, y1,y2,y3..yk形式,前s小的答案满足s % x在集合y1, y2, y3 ... yk中 思路:LRJ大白例题,分两种情况讨论 1.所有x之积较小时候,暴力枚举每个集合选哪个y,然后中国剩余定理求解 2.所有x之积较大时候,选定一个k/x尽可能小的序列,枚举x * t + y (t = 1, 2, 3...)去暴力求解. 代码: #include <stdio.h> #include <string.

对于中国剩余定理(CRT)的初步理解

以前觉得用中国剩余定理来求同余方程组很鸡肋,因为可以用拓展欧几里得算法来构造出一种更加强大(可以处理取模的数(默认为mi)不互质的情况)的算法. 今天查了点资料,发现我太天真了. 首先讲讲中国剩余定理: 即 : x ≡ a[i] (mod m[i]) 1<= i <= r (m[i] 两两互质) 求这个同余方程组可以快速算: x = ∑M/m[i] * Inv(M/m[i], m[i]) * a[i] (mod M) 其中M = m[1]*m[2]*m[3]...m[r]  , Inv(x,

poj1006 ( hdu1370 ):中国剩余定理裸题

裸题,没什么好说的 第一个中国剩余定理 写暴力都过了..可见这题有多水 代码: #include<iostream> #include<stdio.h> #include<math.h> #include<string> #include<map> #include<algorithm> using namespace std; #define MAX 200000000 #define ull unsigned long long

hdu 4878 ZCC loves words(AC自动机+dp+矩阵快速幂+中国剩余定理)

hdu 4878 ZCC loves words(AC自动机+dp+矩阵快速幂+中国剩余定理) 题意:给出若干个模式串,总长度不超过40,对于某一个字符串,它有一个价值,对于这个价值的计算方法是这样的,设初始价值为V=1,假如这个串能匹配第k个模式串,则V=V*prime[k]*(i+len[k]),其中prime[k]表示第k个素数,i表示匹配的结束位置,len[k]表示第k个模式串的长度(注意,一个字符串可以多次匹配同意个模式串).问字符集为'A'-'Z'的字符,组成的所有的长为L的字符串,

中国剩余定理详解

中国剩余定理 孙子算经里有这样一个问题:"今有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二,问物几何?" 翻译成现在的数学问题就是 x%3 == 2,x%5 == 3,x%7 ==  2,求x 的值: 遇到这这样一个问题很多C语言初学者不禁会想到用暴力可以算出来,还要这样一个定理干嘛? 如果数据相当大呢?计算机就会计算相当困难.然而这个问题早早的就被孙子解决了. 求出3,5,7 两两中的最小公倍数lcm,k*lcm与另一个数mod等于1(找出一个符合条件的k): 用k*lcm*