zoukankan      html  css  js  c++  java
  • bzoj1407,洛谷2421 NOI2002荒岛野人

    题目大意:

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

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

    奇怪的是,虽然野人有很多,但没有任何两个野人在有生之年处在同一个山洞中,使得小岛一直保持和平与宁静,这让科学家们很是惊奇。他们想知道,至少有多少个山洞,才能维持岛上的和平呢?

    输入输出格式
    输入格式:
    第1行为一个整数N(1<=N<=15),即野人的数目。

    第2行到第N+1每行为三个整数Ci, Pi, Li (1<=Ci,Pi<=100, 0<=Li<=106 ),表示每个野人所住的初始洞穴编号,每年走过的洞穴数及寿命值。

    输出格式:
    仅包含一个数M,即最少可能的山洞数。输入数据保证有解,且M不大于10^6。

    其实这道题和洛谷的1516非常像。

    那么对于这道题,我们只需要枚举一下这个m,然后(n^2 imes log)判断就可以,我们对于一个枚举到的m

    实际上是求解$$c_i+p_ix=c_j+p+jx pmod m$$

    那么进行一波操作

    [(p_i-p_j)*x+m*y=c_j-c_i ]

    直接exgcd求解即可

    如果最终求得的x(min(l_i,l_j))

    需要注意的是!!!求最小正整数解的时候!!要注意模数小于0的情况

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #define ll long long
    
    using namespace std;
    
    inline int read()
    {
      int x=0,f=1;char ch=getchar();
      while (!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();}
      while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
      return x*f;
    }
    
    const int maxn = 21;
    
    ll c[maxn],p[maxn],l[maxn];
    ll x,y;
    int n;
    
    ll gc(ll a,ll b)
    {
    	if (b==0) return a;
    	else return gc(b,a%b);
    }
    
    ll exgcd(ll &x,ll &y,ll a,ll b)
    {
    	if (b==0)
        {
        	x=1;
        	y=0;
        	return a;
    	}
    	ll cnt = exgcd(x,y,b,a%b);
    	ll tmp = x;
    	x=y;
    	y=tmp-a/b*y;
    	return cnt;
    }
    
    bool check(ll m)
    {
    	for (int i=1;i<=n;i++)
    	  for (int j=i+1;j<=n;j++)
    	  {
    	    ll gcd=exgcd(x,y,p[i]-p[j],m);   
    	    if ((c[j]-c[i])%gcd!=0) continue;
    		ll tmp = m/gcd;
    		tmp=abs(tmp);
    	    x=x%tmp*((c[j]-c[i])/gcd)%tmp;
    	    //cout<<tmp<<"gg"<<endl;
    	    x=(x%tmp+tmp)%tmp;
    	    if (!x) x+=tmp;
    	    //if (x>0) cout<<i<<" "<<j<<" "<<x<<endl;
    	    if (x<=min(l[i],l[j])) return false;
    	  }
    	return true;
    }
    
    ll ans=1;
    
    int main()
    {
      scanf("%d",&n);
      for (int i=1;i<=n;i++) scanf("%lld%lld%lld",&c[i],&p[i],&l[i]),ans=max(ans,c[i]);
      for (ll i=ans;;i++)
      {
      	if (check(i))
      	{
      		cout<<i;
      		return 0;
    	  }
      }
      //cout<<-1<<endl;
      return 0;
    }
    
    
  • 相关阅读:
    luogu4365 秘密袭击 (生成函数+线段树合并+拉格朗日插值)
    [模板]左偏树
    luogu4166 最大土地面积 (旋转卡壳)
    bzoj3168 钙铁锌硒维生素 (矩阵求逆+二分图最小字典序匹配)
    [模板]矩阵树定理
    [模板]快速傅里叶变换(FFT)
    [模板]Min_25筛
    [模板]杜教筛
    [模板]莫比乌斯反演
    DrawerLayout—侧拉栏的使用
  • 原文地址:https://www.cnblogs.com/yimmortal/p/10160811.html
Copyright © 2011-2022 走看看