CF-209-div-二 (CF-359A-D)

CF-209-div-2 (CF-359A-D)

CF-359A. Table

题目链接:

http://codeforces.com/problemset/problem/359/A

题目意思:

给一个矩阵n*m,1为好格子,0为坏格子。四个角落没有好格子,每次可以选择一个好格子和四个角落任意一个格子,使得这两个格子组成的矩形全部选中,求最少需要几次能把整个矩阵全部选中。一个格子可以选多次。

解题思路:

简单贪心题。如果好格子在边界则只需两次,否则需要四次(每个角落个字操作一次)。

代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<sstream>
#include<cstdlib>
#include<string>
#include<string.h>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#include<ctime>
#include<bitset>
#define eps 1e-6
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define ll __int64
#define LL long long
#define lson l,m,(rt<<1)
#define rson m+1,r,(rt<<1)|1
#define M 1000000007
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

#define Maxn 55
int save[Maxn][Maxn];


int main()
{
   //freopen("in.txt","r",stdin);
   //freopen("out.txt","w",stdout);
   int n,m;

   while(~scanf("%d%d",&n,&m))
   {
       for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&save[i][j]);
       bool ans=false;

       for(int j=1;j<=m;j++)
       {
           if(save[1][j])
                ans=true;
            if(ans)
                break;
          if(save[n][j])
                ans=true;
          if(ans)
            break;
       }

       if(ans)
        {
            printf("2\n");
            continue;
        }

        for(int i=1;i<=n;i++)
        {
            if(save[i][1])
                ans=true;
            if(ans)
                break;
            if(save[i][m])
                ans=true;
            if(ans)
                break;
        }

        if(ans)
            printf("2\n");
        else
            printf("4\n");

   }
   return 0;
}

CF-359B. Permutation

题目链接:

http://codeforces.com/problemset/problem/359/B

题目意思:

构造一个1~2*n的一个排列的数组,使得Σ|a[2*i-1]-a[2*i]|-|Σ(a[2*i-1]-a[2*i])|=2*k.

解题思路:

数学+构造。

分析知,如果a[2*i-1]>=a[2*i]则|a[2*i-1]-a[2*i]-|a[2*i-1]-a[2*i]|=0,值不会改变。如果a[2*i-1]<a[2*i],则两者相减相隔2倍的a[2*i]-a[2*i-1].所以可以据此构造。如果k=0,则使得每一个a[2*i-1]>a[2*i]即可。如果k!=0,则只需第一项为1,1+k,其余的都使得a[2*i-1]>a[2*i]即可。

代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<sstream>
#include<cstdlib>
#include<string>
#include<string.h>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#include<ctime>
#include<bitset>
#define eps 1e-6
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define ll __int64
#define LL long long
#define lson l,m,(rt<<1)
#define rson m+1,r,(rt<<1)|1
#define M 1000000007
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

#define Maxn 51000

int main()
{
   //freopen("in.txt","r",stdin);
   //freopen("out.txt","w",stdout);

   int n,k;

   while(~scanf("%d%d",&n,&k))
   {
       int p,q;

       if(k) //k不等于0,构造第一项1,k+1其余的为2~2*n之间不为k+1的,且a[2*i-1]>a[2*i]即可
       {
           printf("%d %d ",1,k+1);
           p=2,q=2*n;
       }
       else
       {
           printf("%d %d ",2,1); //如果k=0,为了统一使得k=1,p=3,q=2*n
           k=1;
           p=3,q=2*n;
       }

       for(int i=1;i<n;i++) //其余的一个大,一个小,用两个指针构造即可
       {
           if(p==k+1) //排除k+1的情况
                p++;
           if(q==k+1)
                q--;

           printf("%d %d ",q,p);
           p++;
           q--;
       }
       putchar('\n');
   }
   return 0;
}

CF-359C. Prime Number

题目链接:

http://codeforces.com/problemset/problem/359/C

题目意思:

给一个质数X,和n个数a1<=a2<=...<=an.求CF-209-div-二 (CF-359A-D)通分得到分母t=x^(a1+a2+...+an),分子为s,求s,t的最大公约数。(ai<=10^9,n<=3*10^5).

解题思路:

快速幂+数学。

由于数据范围很大,硬搞肯定是不行的。分析发现,分母为x^sum,记sum=a1+a2+...+an,分子的各项为x^(sum-ai).显然分子中每项一定可以提出一个min(a^(sum-ai))出来,此时如果min(a^(sum-ai))有x的倍数个,还需向前进位。

代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<sstream>
#include<cstdlib>
#include<string>
#include<string.h>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#include<ctime>
#include<bitset>
#define eps 1e-6
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define ll __int64
#define LL long long
#define lson l,m,(rt<<1)
#define rson m+1,r,(rt<<1)|1
#define M 1000000007
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

#define Maxn 110000
#define M 1000000007
ll save[Maxn],n,x;

ll quick(ll a,ll b)
{
    ll res=1;

    while(b)
    {
        if(b&1)
            res=(res*a)%M;
        a=(a*a)%M;
        b>>=1;
    }
    return res;
}

int main()
{
   //freopen("in.txt","r",stdin);
   //freopen("out.txt","w",stdout);

   while(~scanf("%I64d%I64d",&n,&x))
   {
       ll sum=0;

       for(int i=1;i<=n;i++)
       {
           scanf("%I64d",&save[i]);
           sum+=save[i];
       }
       ll ans=sum-save[n]; //最小的次数
       ll ord=n,cnt=0; //从最大的开始,减了过后,也就是最小的

       for(ll i=save[n];i>=1;i--)
       {
            while(ord>=1&&i==save[ord])
            {
                cnt++;
                ord--;
            }
            if(cnt%x)
                break;
            ans++; //注意每次只能加一个。
            cnt/=x;
       }
       if(ans>sum)
            ans=sum;
       printf("%I64d\n",quick(x,ans));


   }
   return 0;
}

CF-359D. Pair of Numbers

题目链接:

http://codeforces.com/problemset/problem/359/D

题目意思:

给n(n<=3*10^5)个数,求最长的区间,使得该区间内的所有数都能被该区间某一数整除。求该区间的个数及开始位置。

解题思路:

简单dp.(n*lgn).

le[i]表示i作为最小的整除数向左能够连续整除的最小位置。

ri[i]表示i作为最小的整除数向右能够连续整除的最大位置。

利用这条性质构建dp方程:如果save[j]%save[i]==0,则save[j]能整除的范围,save[i]一定可以整除。以区间形式向前递推,优化。

代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<sstream>
#include<cstdlib>
#include<string>
#include<string.h>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<stack>
#include<list>
#include<queue>
#include<ctime>
#include<bitset>
#define eps 1e-6
#define INF 0x3f3f3f3f
#define PI acos(-1.0)
#define ll __int64
#define LL long long
#define lson l,m,(rt<<1)
#define rson m+1,r,(rt<<1)|1
#define M 1000000007
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

#define Maxn 400000

int le[Maxn],ri[Maxn],save[Maxn],n;
int ans[Maxn];
vector<int>myv;

int main()
{
   //freopen("in.txt","r",stdin);
   //freopen("out.txt","w",stdout);

   while(~scanf("%d",&n))
   {
       for(int i=1;i<=n;i++)
       {
           scanf("%d",&save[i]);
           ans[i]=0;
           le[i]=ri[i]=i;
       }
       myv.clear();
       for(int i=2;i<=n;i++)
       {
           int tt=i-1;

           while(save[tt]%save[i]==0&&tt>=1) //如果能整除,它包含的区间也一定能被save[i]整除
               tt=le[tt]-1;
           le[i]=le[tt+1];
       }
       for(int i=n-1;i>=1;i--)
       {
           int tt=i+1;
           while(save[tt]%save[i]==0&&tt<=n)
           {
               tt=ri[tt]+1;
           }
           ri[i]=ri[tt-1];

       }
       for(int i=1;i<=n;i++) //统计区间
       {
           int temp=le[i],len=ri[i]-le[i];
            if(len>ans[temp])
                ans[temp]=len;
       }

       int Max=0;

       for(int i=1;i<=n;i++) //找到最大的
       {
           if(ans[i]>Max)
           {
               Max=ans[i];
               myv.clear();
               myv.push_back(i);
           }
           else if(ans[i]==Max)
                myv.push_back(i);

       }
       /*for(int i=1;i<=n;i++)
       {
           if(ans[i]==Max)
                myv.push_back(i);
       }*/
       printf("%d %d\n",myv.size(),Max);
       for(int i=0;i<myv.size();i++)
            printf("%d ",myv[i]);
       putchar('\n');


   }

   return 0;
}