zoukankan      html  css  js  c++  java
  • 【模拟题(电子科大MaxKU)】解题报告【树形问题】【矩阵乘法】【快速幂】【数论】

    目录:

       1:一道简单题【树形问题】(Bzoj 1827 奶牛大集会)

       2:一道更简单题【矩阵乘法】【快速幂】

       3:最简单题【技巧】

       话说这些题目的名字也是够了。。。。


    题目:

    1、一道简单题

    时间1s

    题目描述

             Bessie正在计划一年一度的奶牛大集会,来自全国各地的奶牛将来参加这一次集会。当然,她会选择最方便的地点来举办这次集会。每个奶牛居住在 N(1<=N<=100,000) 个农场中的一个,这些农场由N-1条道路连接,并且从任意一个农场都能够到达另外一个农场。道路i连接农场A_i和B_i(1 <= A_i <=N; 1 <= B_i <= N),长度为L_i(1 <= L_i <= 1,000)。集会可以在N个农场中的任意一个举行。另外,每个牛棚中居住着C_i(0 <= C_i <= 1,000)只奶牛。在选择集会的地点的时候,Bessie希望最大化方便的程度(也就是最小化不方便程度)。比如选择第X个农场作为集会地点,它的不方便程度是其它牛棚中每只奶牛去参加集会所走的路程之和,(比如,农场i到达农场X的距离是20,那么总路程就是C_i*20)。帮助Bessie找出最方便的地点来举行大集会。  

    输入

    第一行:一个整数N

    第二到N+1行:第i+1行有一个整数C_i

    第N+2行到2*N行,第i+N+1行为3个整数:A_i,B_i和L_i。

    输出

    一个数字表示答案

    样例

    Input

    Output

    5
    1
    1
    0
    0
    2
    1 3 1
    2 3 2
    3 4 3
    4 5 3

    15

    对于

    20%数据n<20

    50%数据 n<2000

    100%数据n<100,000


    2、一道更简单题

    时间1s

    描述

    有n个数围成一圈,每次操作后每个数变成和他距离在d以内的数字之和,求k次操作后每个数字模1000000007分别是多少有n个数围成一圈,每次操作后每个数变成和他距离在d以内的数字之和,求k次操作后每个数字模1000000007分别是多少

    输入

    第一行三个数n, d, k (1 ≤ n ≤ 1000, 0 ≤ d < n / 2 , 1 ≤ k ≤ 100 000 000),

    第二行有n个正数,每个的大小都在int范围内

    输出

    一行n个数,空格隔开,表示结果。

    样例

    Input

    Ouput

    5 1 1
    1 2 10 3 6

    9 13 15 19 10

    10%数据满足n*k<10^6

    30%数据满足 n<=100

    50%数据满足 n<=500

    100%数据满足n<=1000


    3、最简单的题

    时间1s

    题目描述

    给出正整数n和k,计算j(n, k)=k mod 1 + k mod 2 + k mod 3 + … + k mod n的值,其中k mod i表示k除以i的余数。

    例如j(5, 3)=3 mod 1 + 3 mod 2 + 3 mod 3 + 3 mod 4 + 3 mod 5=0+1+0+3+3=7

    输入

    第一行2个数n,k。

    输出

    一个数 j(n,k)。

    样例

    Input

    Output

    5 3

    7

     

    50% 数据 满足 n<1e6

    100%数据 满足 n<1e12 k<=n


    解题报告:

      第一题:参考Regina8023(CSDN)的思路解释:

      发现这些农场构成了一棵

      所以首先以任意一个点为根建树,并把这个点当做目标点。

      那么最终的答案就是每条树边乘有几头牛要走,一条边有几头牛要走其实就是这条边下面有几个子孙。

      于是我们预处理出以1为根的树的答案,并求出每个节点有几个子孙,然后O(1)转移到他的儿子的答案,这个儿子又可以O(1)转移到他自己的儿子。。。

      因此我们用O(n)时间就求出以每个点为目的地的路程和,输出最小即可。

      转移方法是:

      根变成儿子的时候,只有连接儿子的边要变化,根据预处理出的每个节点的儿子,即可转移。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<string>
     5 #define LL long long
     6 using namespace std;
     7 const int maxn=100005;
     8 LL n,ans=1e15,sum,c[maxn];
     9 LL he[maxn],ne[maxn*2],to[maxn*2],w[maxn*2],tot;
    10 LL f[maxn],cost[maxn],num[maxn],len[maxn];
    11 void add(LL a,LL b ,LL x)
    12 {
    13     tot++;
    14     ne[tot]=he[a];
    15     w[tot]=x;
    16     to[tot]=b;
    17     he[a]=tot;
    18 }
    19 void ready(LL x ,LL fa)//当前节点x,它的father fa 
    20 {
    21     for (LL  i=he[x];i;i=ne[i])
    22       if (to[i]!=fa)//!!! 防止原路返回 
    23       {
    24           len[to[i]]=len[x]+w[i];
    25           ready(to[i],x);//从上到下 
    26           cost[x]+=cost[to[i]];//从x到根节点(包括它的所有儿子)的总路程 
    27           num[x]+=num[to[i]];//从x向下的所有奶牛数 
    28       }
    29     cost[x]+=len[x]*c[x];//加上自己 
    30     num[x]+=c[x];
    31 }
    32 void dfs(LL x,LL fa)
    33 {
    34     for (LL i=he[x];i;i=ne[i])
    35     if (to[i]!=fa)
    36     {
    37         f[to[i]]=f[x]+(sum-num[to[i]])*w[i]-num[to[i]]*w[i];
    38         //将集合地点向下移,用O(1)算它的总路程 
    39         dfs(to[i],x);    
    40     }
    41 }
    42 int main()
    43 {
    44     freopen("A.in","r",stdin);
    45     freopen("A.out","w",stdout);
    46     cin>>n;
    47     for (LL i=1;i<=n;i++)
    48     {
    49         scanf("%I64d",&c[i]);
    50         sum+=c[i];
    51     }
    52     for (LL i=1;i<n;i++)// < n  not <=n 
    53     {
    54         LL a,b,x;
    55         scanf("%I64d%I64d%I64d",&a,&b,&x);
    56         add(a,b,x);
    57         add(b,a,x);
    58     }
    59     ready(1,0);//预处理任意根节点的一棵树 
    60     f[1]=cost[1];
    61     dfs(1,0);
    62     for (LL i=1;i<=n;i++)
    63     {
    64         if (f[i]<ans)
    65           ans=f[i];
    66     } 
    67     cout<<ans;
    68     return 0;
    69 }
    View Code

      第二题:如果纯模拟的话,按理说可以得10分,但是我的纯模拟怎么一分都没有。这道题要用矩阵乘法+快速幂。具体的推导方法是这样的:

      首先,我们从一个点的多次修改得到系数:

      如 a0,a1,a2,a3,a4  

         第一次:a0=a0+a1+a4 第二次:a0=3*a0+2*a1+2*a4+a2+a3第三次:a0=7*a0+6*a1+6*a4+4*a2+4*a3

           a1=a0+a1+a2     a1=3*a1+2*a0+2*a2+a3+a4    ......

           a2=a1+a2+a3           a2=3*a2+2*a3+2*a1+a4+a0    ......

           ......          ......

      按照a0~a4提取出它们的系数:1*a0 1*a1 0*a2 0*a3 1*a4  

                    3*a0 2*a1 1*a2  1*a3 2*a4

                     7*a0 6*a1 4*a2  4*a3 6*a4

      所以我们想到了矩阵相乘用   1 1 0 0 1     a0  a0+a1+a4

                   1 1 1 0 0  a1  a0+a1+a2

                   0 1 1 1 0  *  a2 =   a1+a2+a3

                   0 0 1 1 1  a3  a2+a3+a4

                   1 0 0 1 1  a4  a3+a4+a1

      如果k>1的话,就要再用新的数列重复以上操作,所以,运用矩阵乘法的结合律,我们可以用快速幂求解。

      又发现左边矩阵很有规律,所以可以简化为一维数组,具体的解释和注意在代码中了。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #define LL long long
     5 using namespace std;
     6 const LL INF=1000000007;
     7 const LL maxn=1005;
     8 LL n,d,k;
     9 LL num[maxn],tmp[maxn];
    10 void cheng(LL a[],LL b[])//a num ,b tmp
    11 {
    12     LL c[maxn];
    13     memset(c,0,sizeof(c));//局部变量 c 初值随机,需要重定为 0 
    14     for (LL i=0;i<n;i++)//c[0]=a0*b0+a1*b1+a2*b2+a3*b3+a4*b4
    15       for (LL j=0;j<n;j++)//c[1]=a0*b4+a1*b0+a2*b1+a*b2+a4*b3
    16       {
    17           if (i>j) c[i]=(c[i]+a[j]*b[n+j-i])%INF;//发现当i>j时,b取b[n+j-i] 
    18           else
    19               c[i]=(c[i]+a[j]*b[j-i])%INF;//当i<=j时,b取 b[j-i]
    20       }
    21     for (LL i=0;i<n;i++)
    22       a[i]=c[i]%INF;
    23 }
    24 int main()
    25 {
    26     freopen("B.in","r",stdin);
    27     freopen("B.out","w",stdout);
    28     cin>>n>>d>>k;
    29     for (LL i=0;i<n;i++)
    30       scanf("%d",&num[i]);
    31     tmp[0]=1;
    32     for (LL i=1;i<=d;i++)
    33         tmp[i]=tmp[n-i]=1;
    34     while (k>0)
    35     {
    36         if (k&1) cheng(num,tmp);
    37         k=k>>1;
    38         cheng(tmp,tmp);
    39     }
    40     for (LL i=0;i<n;i++)
    41       printf("%I64d ",num[i]);
    42     return 0;
    43 }
    View Code

       第三题:主要是打表找规律,比如从n=100,k=100打表出100/(1~100)=x.....y 从中找到规律:

      100/(51~100)=1....(0、1~48、49)

      100/(34~50)=2......(0、2~30、32)

      100/(26~33)=3......(1,4~19,22)

      100/(21~25)=4......(0,4,~12,16)

      100/(17~20)=5......(0,5,10,15)

      100/(15,16)=6......(4,10)

      .......

      从100/12 开始余只有一个。所以从1~12就循环求解。另外,当n>k时,直接余数=k 加上去就可以了。

     1 #include<iostream>
     2 #include<cstdio>
     3 #define LL long long
     4 using namespace std;
     5 LL n,k;
     6 LL ans;
     7 int main()
     8 {
     9     freopen("C.in","r",stdin);
    10     freopen("C.out","w",stdout);
    11     cin>>n>>k;
    12     if (n>k)
    13     {
    14         ans+=(n-k)*k;
    15         n=k;
    16     }
    17     LL l,x,d,next,r;
    18     for (LL i=n;i>=1;i=next)
    19     {
    20         d=k/i;//等差值 
    21         l=k%i;//a(1) 
    22         next=k/(d+1);//下一个区间的右边界 
    23         x=i-next;//区间个数 n
    24         r=k%(next+1);//a(n) 
    25         if (l==r) break;
    26         else
    27         //S(n)=n*a(1)+( n*(n-1)/2 )*d
    28             ans+=x*l+(x*(x-1)/2)*d;
    29     }
    30     for (LL i=1;i<=next+1;i++)
    31       ans+=k%i;
    32     cout<<ans;
    33     return 0;
    34 }
    View Code

     

  • 相关阅读:
    Hbase-06-Snapshot原理
    Hbase-05-备份表数据
    Hbase-04-hbck
    Python Exception Handling
    10.TiPD 调度
    8.存储引擎TiFlash
    6.TiDB数据库的存储
    7.存储引擎TiKV
    4.Tidb SQL优化(一)
    5.TiDB SQL优化(二)
  • 原文地址:https://www.cnblogs.com/lx0319/p/5811863.html
Copyright © 2011-2022 走看看