zoukankan      html  css  js  c++  java
  • NOI 2002 荒岛野人

    人生第一次做NOI的题祭!!!

    大概是NOI最简单的一道题

    克里特岛以野人群居而著称。岛上有排列成环行的M个山洞。这些山洞顺时针编号为1,2,…,M。岛上住着N个野人,一开始依次住在山洞C1,C2,…,CN中,以后每年,第i个野人会沿顺时针向前走Pi个洞住下来。

    每个野人i有一个寿命值Li,即生存的年数。

    下面四幅图描述了一个有6个山洞,住有三个野人的岛上前四年的情况。三个野人初始的洞穴编号依次为1,2,3;每年要走过的洞穴数依次为3,7,2;寿命值依次为4,3,1。

    (图片就不考了)

    我们发现这道题奇怪的给了答案的数据规模,这就说明我们可以枚举答案。

    然后我们发现n的数据规模很小,n^2*m似乎可以过。

    我们对于每一个答案,枚举任意两个人是否不会相遇。

    对于每两个野人,我们可以根据他们的C和P列出同余方程:

    Ci+Pi*x≡Cj+Pj*x(mod b)  其中x是年数,b是枚举的答案。

    然后,将这个同余方程转化为不定方程,然后求解。

    Ci+Pi*x=Cj+Pj*x+b*-y(-y是一个构造的新的未知数)

    (Pi-Pj)*x+b*y=Cj-Ci

    然后用拓展欧几里得求解(这是解析)求解

    最后一定要注意!!!(因为在这里卡了几个小时)求完特解以后,要保证最终解为正。

    通常情况,只需要(x*(Cj-Ci)/d(最小公约数)+b/d)%b/d就可以了。

    但是注意Cj-Ci可能为负,而且c++的负数取模的机制是取决于被模数,被模数为正则余数为正,为负则反之。

    而因为Cj-Ci可能为负,可能导致最终答案依然为负。

    故此,我们将x*(Cj-Ci)/d先模b/d,保证其绝对值是小于b/d的,但是,b/d也可能为负(因为d可能为负),所以即使加上一个b/d也不能保证结果为正。

    因此,我们把最后加上的b/d变成一个它的倍数且保证它为正的,可以替换为b或者b/d的绝对值。

    代码如下:

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #define MAXN 10010
    #define in(a) a=read()
    #define REP(i,k,n)  for(int i=k;i<=n;i++)
    using namespace std;
    inline int read(){
        int x=0,f=1;
        char ch=getchar();
        for(;!isdigit(ch);ch=getchar())
            if(ch=='-')
                f=-1;
        for(;isdigit(ch);ch=getchar())
            x=x*10+ch-'0';
        return x*f;
    }
    int n,m;
    int c[100010],p[100010],l[100010];
    int t;
    inline void exgcd(int a,int b,int &d,int &x,int &y){
        if(b==0)  x=1,y=0,d=a;
        else  exgcd(b,a%b,d,x,y),t=x,x=y,y=t-a/b*y;
    }
    inline bool check(int k){
        REP(i,1,n)
            REP(j,i+1,n){
                int a=p[i]-p[j],b=k,x,y,d;
                exgcd(a,b,d,x,y);
                if((c[j]-c[i])%d!=0)  continue;
                x=((x*(c[j]-c[i])/d)%(b/d)+(abs(b/d)))%(b/d);//保证x为正
                if(x<=min(l[i],l[j]))  return 0;
            }
        return 1;
    }
    int main(){
        in(n);
        REP(i,1,n)  in(c[i]),in(p[i]),in(l[i]),m=max(m,c[i]);
        REP(i,m,1000000) if(check(i)){ cout<<i<<endl;  return 0;}
        return 0;
    }
  • 相关阅读:
    HTML DOM 教程Part5 [DOM 冷门对象] 摘录自W3C School
    Javascript DOM 的节点操作示例
    CSS 教程Part2 [背景、文本、字体](摘录自 W3C School)
    Stream Part.1
    Thread.Join() 方法
    CSS 教程Part3 [列表、表格、轮廓](摘录自 W3C School)
    使用委托异步调用方法让程序并行运行
    JSON 数据格式
    C# WinForm 程序中获取本机IP地址
    微软示例代码 for ManualResetEvent
  • 原文地址:https://www.cnblogs.com/jason2003/p/10594021.html
Copyright © 2011-2022 走看看