zoukankan      html  css  js  c++  java
  • Individual Contest #1 and Private Training #1

    第一次的增补赛,也是第一场个人排位赛,讲道理打的和屎一样,手速题卡了好久还WA了好多发,难题又切不出来,这种情况是最尴尬的吧!

    Individual Contest #1:

    Ploblem D:

    题意:给你一个含有N个元素的数组和操作次数m,然后求出经过m次操作后,从区间X到Y的总和;操作如下:{1,6,9}==>{1,7,6,15,9}(多CASE)

    题目链接:https://www.codechef.com/problems/CHSPARR

    思路:

           首先,最容易想到的是单纯的模拟每一次这样的操作然后用数组存起来,再去算区间X到Y的总和。可是,当N和m取最大的时候,数组元素数量会达到10^15,直接GG!所以得换个套路!

    对于原始数组{1,6,9},元素对应的下标各自为1,2,3。以1,6为例,{1,6}==>{1,7,6}==>{1,8,7,13,6}==>{1,9,8,15,7,20,13,19,6},元素6的标和m的变化为:i(0),2*i-1(1),4*i-3(2),8*i-7(3)

    把两者联系起来你可以发现经过m次变换后元素6的下标为:i*2^m-(2^m-1)==>(i-1)*2^m-1。

           接着继续模拟:{1,6}==>{1,7(1+6),6}==>{1,8(1,1+6),7(1+6),13(1+6,6),6},你会发现,如果把1和6中间的所有新增加的元素看成一个整体s,那个他每一次都会

    向左向右复制一遍,再加上他本身,就是3*s,同时端点处的1和6也会每一次都增加一次,如果能将s和(1+6)联系起来,那么1和6之间的元素总和不就可以直接求了吗!到这里也可以看出

    来是倍数关系。设数组k表示分别经过1-m次操作后对应的系数,则k[m]=k[m-1]*3+1。这样的话如果X和Y里面有包含一个或多个完整的中间元素块,就可以直接求出来了!

           那多出来的那部分怎么办呢?你会发现对于元素a(i)和元素b(j),元素a+b一定在(i+j)/2的位置。所以直接分治,每一次都缩小规模找到完整块++,代码如下:

    #include <iostream>
    #include <queue>
    #include <stack>
    #include <cstdio>
    #include <vector>
    #include <map>
    #include <set>
    #include <algorithm>
    #include <cmath>
    #include <cstring>
    #include <cstdlib>
    #include <string>
    #include <sstream>
    #define lson l,m,rt*2
    #define rson m+1,r,rt*2+1
    #define mod 1000000007
    using namespace std;
    typedef long long LL;
    using namespace std;
    LL a[100006],k[40],n,m,x,y;
    void init()
    {
        k[0]=0;
        for(int i=1;i<=30;i++)
        {
            k[i]=(k[i-1]*3+1)%mod;
        }
    }
    LL f(LL num)
    {
        if(num==0)return 0;
        int i,j,mm;
        LL res=0,p=1<<m,q,h,mid,l,r;
        q=num/p;
        for(i=1;i<=q;i++)
        {
            res=(res+(a[i]+a[i+1])*k[m]+a[i])%mod;
        }
        h=num%p;
        l=a[q+1];
        r=a[q+2];
        mm=m;
        while(h>0&&mm>0)
        {
            mm--;
            mid=1<<mm;
            if(h>=mid)
            {
                res=(res+(2*l+r)*k[mm]+l)%mod;
                h-=mid;
                l=l+r;
            }
            else
            {
                r=l+r;
            }
        }
        //if(h!=0)res+=r;
        return res%mod;
    }
    int main()
    {
    #ifdef Local
        freopen("data.txt","r",stdin);
    #endif
        int i,j,T;
        LL ans;
        cin>>T;
        init();
        while(T--)
        {
            cin>>n>>m>>x>>y;
            for(i=1;i<=n;i++)
            {
                scanf("%lld",&a[i]);
            }
            ans=f(y)-f(x-1);
            if(ans<0)ans+=mod;
            printf("%lld
    ",ans);
        }
    }
    View Code

    Ploblem H:

    题意:给你一棵N节点的树和K种颜色的颜料,对这样的一棵树进行染色,使得任意两个相同颜色的联通节点之间所有节点颜色相同,求出所有的染色方法。

    题目链接:https://www.codechef.com/problems/COLTREE

    思路 I:

           其实就是给你一棵树,让你切分成m棵子树,每棵子树的染色相同,有多少种染色方法。切成m棵子树,其实就是选择m-1条边去切断,所有就是C(n-1,m-1),

    然后对于m棵子树进行K种染色,其实就是A(K,m),所有方法数就是C(n-1,m-1)*A(K,m)。然后对于规模不大的大组合数取模,用杨辉三角把除变加进行

    处理就可以了。

     1 #include <iostream>
     2 #include <queue>
     3 #include <stack>
     4 #include <cstdio>
     5 #include <vector>
     6 #include <map>
     7 #include <set>
     8 #include <algorithm>
     9 #include <cmath>
    10 #include <cstring>
    11 #include <cstdlib>
    12 #include <string>
    13 #include <sstream>
    14 #define lson l,m,rt*2
    15 #define rson m+1,r,rt*2+1
    16 #define mod 1000000007
    17 using namespace std;
    18 typedef long long LL;
    19 LL C[60][60];
    20 LL A(int n,int m)
    21 {
    22     LL res=1;
    23     for(int i=n;i>n-m;i--)
    24     {
    25         res=(res*i)%mod;
    26     }
    27     return res;
    28 }
    29 void init()
    30 {
    31     memset(C,0,sizeof(C));
    32     for(int i=0;i<=50;i++)C[i][0]=1;
    33     for(int i=1;i<=50;i++)
    34     {
    35         for(int j=1;j<=i;j++)
    36         {
    37             if(j==1)C[i][j]=i;
    38             else if(i==j)C[i][j]=1;
    39             else C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
    40         }
    41     }
    42 }
    43 int main()
    44 {
    45 #ifdef Local
    46     freopen("data.txt","r",stdin);
    47 #endif
    48      int n,m,u,v,T,i,j;
    49      LL ans;
    50      init();
    51      cin>>T;
    52      while(T--)
    53      {
    54          ans=0;
    55          cin>>n>>m;
    56          for(i=0;i<n-1;i++)scanf("%d%d",&u,&v);
    57          for(i=1;i<=min(n,m);i++)
    58          {
    59              ans=(ans+C[n-1][i-1]*A(m,i))%mod;
    60          }
    61          cout<<ans<<endl;
    62      }
    63 }
    View Code

    思路 II:树形DP(等我学会了再更...)

    Private Training #1

    Problem C (UVALive 6602   Counting Lattice Squares

    题意:给你一个(n+1)*(m+1)的点矩阵,然后让你在点阵里面找出,所有面积为奇数的正方形的个数?

    题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4613

    思路:

          首先,面积为奇数的正方形,可以直接由边长为奇数K得来,也可以由类似这样sqrt(K)的边得来。然后对于第二种情况的K,可以由a+b的和为奇数的情况得来,即

    K=sqrt(a*a+b*b)&&(a+b)%2==1。接着你会发现,边长为sqrt(K)的正方形需要的空间是边长为(a+b)的正方形,所以边长为sqrt(K)的情况可以把它当做边长为

    (a+b)的一类型,且该区域一共可以画2个不同的边长为sqrt(K)的正方形。然后对于一个(n+1)*(m+1)的点矩阵,一共可以切成(n-i+1)*(m-i+1)种边长为i的正方

    形,但是对于边长i,它可以由不同的a+b组合而来,假设i=5,那么5=1+4=2+3,6=1+5=2+4=3+3,所以最后的公式就是(n-i+1)*(m-i+1)*(i/2*2(2种画法)+1(本身))

     1 #include <iostream>
     2 #include <queue>
     3 #include <stack>
     4 #include <cstdio>
     5 #include <vector>
     6 #include <map>
     7 #include <set>
     8 #include <algorithm>
     9 #include <cmath>
    10 #include <cstring>
    11 #include <cstdlib>
    12 #include <string>
    13 #include <sstream>
    14 #define lson l,m,rt*2
    15 #define rson m+1,r,rt*2+1
    16 #define mod 1000000007
    17 using namespace std;
    18 typedef long long LL;
    19 int main()
    20 {
    21 #ifdef Local
    22     freopen("data.txt","r",stdin);
    23 #endif
    24     int i,j;
    25     LL ans,s,n,m;
    26     while(~scanf("%lld %lld",&n,&m))
    27     {
    28         if(!n&&!m)break;
    29         ans=0;
    30         for(i=1;i<=min(n,m);i+=2)
    31         {
    32             ans+=((n-i+1)*(m-i+1)*(i/2*2+1));
    33         }
    34         cout<<ans<<endl;
    35     }
    36 
    37 }
    View Code

    Ploblem J:(UVALive 6609  Minimal Subarray Length

    题意:给你一个含有N个元素的数组和一个价值X,使得数组中连续一段数字和大于等于X的长度最小,如果找不到则输出-1

    题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4620

    思路:

           先用预处理出元素1-i(i<=n)的元素总和sum[i],再用RMQ预处理出dp[i][j]表示区间从i开始,长度为2^j的最大sum,然后再对于每一个i(1-n)二分(query(l,r)-sum[i-1])

    >=X,的最短长度,复杂度是n(logn)。

     1 #include <iostream>
     2 #include <queue>
     3 #include <stack>
     4 #include <cstdio>
     5 #include <vector>
     6 #include <map>
     7 #include <set>
     8 #include <algorithm>
     9 #include <cmath>
    10 #include <cstring>
    11 #include <cstdlib>
    12 #include <string>
    13 #include <sstream>
    14 #define lson l,m,rt*2
    15 #define rson m+1,r,rt*2+1
    16 #define mod 1000000007
    17 using namespace std;
    18 typedef long long LL;
    19 const int INF=0x3f3f3f3f;
    20 LL dp[500005][20],sum[500005];
    21 void RMQ(int n)
    22 {
    23     int i,j;
    24     for(i=1;i<=n;i++)dp[i][0]=sum[i];
    25     for(j=1;(1<<j)<=n;j++)
    26     {
    27         for(i=1;(i+(1<<j))<=n;i++)
    28         {
    29             dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
    30         }
    31     }
    32 }
    33 LL query(int l,int r)
    34 {
    35     int p=0;
    36     while(1<<(p+1)<=(r-l+1))p++;
    37     return max(dp[l][p],dp[r-(1<<p)+1][p]);
    38 }
    39 int main()
    40 {
    41 #ifdef Local
    42     freopen("data.txt","r",stdin);
    43 #endif
    44     int i,j,T,n;
    45     LL k;
    46     cin>>T;
    47     while(T--)
    48     {
    49         memset(sum,0,sizeof(sum));
    50         cin>>n>>k;
    51         for(i=1;i<=n;i++)
    52         {
    53             scanf("%lld",&sum[i]);
    54             sum[i]+=sum[i-1];
    55         }
    56         RMQ(n);
    57         int ans=INF;
    58         for(i=1;i<=n;i++)
    59         {
    60             int l=i,r=n;
    61             while(l<=r)
    62             {
    63                 int m=(l+r)/2;
    64                 if((query(i,m)-sum[i-1])>=k)
    65                 {
    66                     r=m-1;
    67                 }
    68                 else l=m+1;
    69             }
    70             if(r<=n&&sum[r]-sum[i-1]>=k)ans=min(ans,r-i+1);
    71             if(l<=n&&sum[l]-sum[i-1]>=k)ans=min(ans,l-i+1);
    72         }
    73         if(ans==INF)cout<<-1<<endl;
    74         else cout<<ans<<endl;
    75     }
    76     return 0;
    77 }
    View Code

    To Be Continue!

             

  • 相关阅读:
    导弹拦截版
    [USACO1.5]数字三角形 Number Triangles
    FBI树
    修复公路
    台阶问题
    阶乘问题
    连续自然数和
    又是毕业季I
    生活大爆炸版石头剪刀布
    曹冲养猪
  • 原文地址:https://www.cnblogs.com/27sx/p/5662104.html
Copyright © 2011-2022 走看看