zoukankan      html  css  js  c++  java
  • 2019牛客暑期多校训练营(第一场)

    传送门

    A.Equivalent Prefixes

    •题意

    有两个数组a,b,现给一个定义,等价:

    在区间[L,R]上的任意一个区间内,ai,bi最小值的位置相等,则称这两个数组是等价的

    给你两个数组a,b,求a,b存在[1,R]是等价的最大的R

    •思路

    利用单调栈,求出每个ai,bi是最小值的左区间

    当左区间不相同时,即可得到答案

    •栗子

    a数组         3    1    5    2    4

    左区间        1    1    3    3    5

    b数组         5    2    4    3    1

    左区间        1    1    3    3    1

    即可得到 R=4

    •代码

    View Code

     

    B.Integration

    •题意

    (109+7)取模的值

    •思路

    裂项相消,利用

    参考这位大佬

    把1/2乘进去得

     然后利用费马小定理

    (p/q)%mod≡p*q-1%mod

    •代码

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define ll long long
     4 const ll mod=1e9+7;
     5 const int maxn=1e3+5;
     6 ll a[maxn],b[maxn];
     7 int n;
     8 
     9 ll quickMod(ll x,ll y)
    10 {
    11     ll res=1;
    12     x=x%mod;
    13     while(y)
    14     {
    15         if(y&1)
    16             res=(res*x)%mod;
    17         x=(x*x)%mod;
    18         y>>=1;
    19     }
    20     return res;
    21 }
    22 
    23 int main()
    24 {
    25     while(scanf("%d",&n)!=EOF)
    26     {
    27         for(int i=1;i<=n;i++)
    28         {
    29             cin>>a[i];
    30             b[i]=(a[i]*a[i])%mod;
    31         }
    32         ll ans=0;
    33         for(int i=1;i<=n;i++)
    34         {
    35             ll mul=a[i]*2;
    36             for(int j=1;j<=n;j++)
    37             {
    38                 if(j!=i)
    39                     mul=(mul*(b[j]-b[i]+mod)%mod)%mod;
    40             }
    41             mul=quickMod(mul,mod-2);
    42             ans=(ans+mul)%mod;
    43         }
    44         cout<<ans<<endl;
    45     }
    46     return 0;
    47 }
    View Code

     

    C.Euclidean Distance

    •题意

    给定一个N维坐标系的点A(a1/m,a2/m,a3/m,...,an/m),
    寻找一个点P(p1,p2,p3,...,pn)满足p点的各坐标之和为1,且p1,p2,p3,...,pn > 0
    使得A点到P点的欧几里得距离最小,
    其中A与P之间的欧几里得距离即为∑i=1(ai​/m−pi​),
    求这个最小的欧几里得距离,若为分数则用分数形式表示。

    •思路

    官方题解用拉格朗日乘子法做的,但是我不会(o(╥﹏╥)o

    翻到这位大佬的博客,数形结合太形象了_(:з」∠)_ 

    对于每个数 我们先不看分母,后来直接除分母就阔以

    我们要使得减去pi后的ai平方和最小,那么是ai中较大的数减小的收益一定比使较小的数变小的收益大

    对于所有的a[i],按从大到小排序,

    来看下排序后的图

    接下来我们需要确定P的坐标,也就是将 -m(因为在欧几里得距离中A和P的坐标是直接相减关系)分配到这个图

    如何分配m呢?依次把最高堆的推向比较低的堆,直到不够再向下一堆(pos+1)的水平线推进为止,我们将m中未分配完称为rest

    这时候未分配的rest再平均分配到所推进的前pos堆里去,也就是把这pos堆再往下推进rest/pos个单位

    如图

     关于分子分母处理看代码中的注释

    •代码

     1 、#include<bits/stdc++.h>
     2 using namespace std;
     3 #define ll long long
     4 const int maxn=1e4+5;
     5 int a[maxn];
     6 int main()
     7 {
     8     int n,m;
     9     while(~scanf("%d%d",&n,&m))
    10     {
    11         for(int i=1;i<=n;i++)
    12             cin>>a[i];
    13         sort(a+1,a+1+n,greater<int>());
    14         ll rest=m;  //p[i]的总分配价值是 1 ,因为 ∑pi=m,也就是 m/m
    15         int pos=1;  //pos 标记能够分配p[i] 到第 pos 个
    16         while(pos<n)
    17         {
    18             if(rest<(a[pos]-a[pos+1])*pos)//如果不能继续向下一水平线推
    19                 break;
    20             rest-=(a[pos]-a[pos+1])*pos;
    21             pos++;
    22         }
    23         
    24         //直到不能再往下一个堆(pos+1)的水平线推,
    25         //那么剩余的可以再让前pos堆水平下推rest/pos个单位
    26         //最终前pos堆的值 都是a[pos]-rest/pos,
    27         //然后分子分母同乘pos,分子变为(a[pos]*pos-rest)
    28         //平方后分子为(a[pos]*pos-rest)²,分母为(m*pos)²
    29         //因为一共有pos堆分配到的,所以∑的结果分子为pos倍的
    30         ll ansa=(a[pos]*pos-rest)*(a[pos]*pos-rest)*pos;
    31         for(int i=pos+1;i<=n;i++)    //分配不到的 a[i],直接加上
    32             ansa+=a[i]*a[i]*pos*pos; //因为分子乘了pos倍,记得平方
    33             
    34         ll ansb=m*m*pos*pos;         //分母(m*pos)² 
    35         ll gcd=__gcd(ansa,ansb);     //求gcd 约分
    36         if(gcd%ansb==0)
    37             cout<<(ansa/ansb)<<endl;
    38         else
    39             cout<<ansa/gcd<<"/"<<ansb/gcd<<endl;
    40     }
    41 }
    View Code

    E.ABBA

    •题意

    有一个2(n+m)的序列,其中包括n各AB子序列,m个BA子序列

    问这个序列有多少种情况,对109+7取模

    •思路

    定义dp[i][j]为有i个A,j个B的方案数

    所以转移方程为

    dp[i+1][j]=dp[i+1][j]+dp[i][j]

    dp[i][j+1]=dp[i][j+1]+dp[i][j]

    那怎么才能保证有n个AB呢?

    前面的A的个数i比B的个数j多n的话,至少会形成n个AB

    同理前面的B的个数j比A的个数多m的话,至少会形成m个BA

    参考此处

    •代码

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define ll long long
     4 const ll mod=1e9+7;
     5 const int maxn=1e3+5;
     6 ll dp[maxn*2][maxn*2];
     7 int n,m;
     8 int main()
     9 {
    10     while(~scanf("%d%d",&n,&m))
    11     {
    12         for(int i=0;i<=n+m;i++)
    13             for(int j=0;j<=n+m;j++)
    14                 dp[i][j]=0;
    15         dp[0][0]=1;
    16 
    17         for(int i=0;i<=n+m;i++)
    18         {
    19             for(int j=0;j<=n+m;j++)
    20             {
    21                 if(i-j<n)//此时小于n个AB
    22                     dp[i+1][j]=(dp[i+1][j]+dp[i][j])%mod;
    23                 if(j-i<m)//此时小于m个BA
    24                     dp[i][j+1]=(dp[i][j+1]+dp[i][j])%mod;
    25             }
    26         }
    27         cout<<dp[n+m][n+m]<<endl;
    28     }
    29 }
    View Code

     

    F.Random Point in Triangle

    •题意

    给定三个顶点的坐标形成一个三角形(也可能不能构成)

    存在一点P使得Sp=max{SPAB,SPBC,SPCA}

    求36*(Sp的数学期望)

    •思路

    正解证明到现在也不会,等到会了再来补上,结论是22倍的面积

    以下是非正解

    纯暴力模拟找规律来着,

    当SPAB=SPBC=SPCA时,P为三角形的重心

     就假设一个很大的三角形,先求出面积,
    然后求出重心,再让去移动整数点,相当于模拟P点在上移。
    一直上移到顶点处,算一下是多少倍的面积
    因为给的坐标是整数,输出答案也是整数,就很容易往面积的偶数倍上去想
    (因为奇数倍可能不是整数
    多测几组数据,然后多wa几发就出来了...
    所以就有了上面的非正解...(大雾

     •代码

    暴力模拟的代码

    #include<bits/stdc++.h>
    using namespace std;
    double gets(int x1,int x2,int x3,int yl,int y2,int y3)
    {
        return abs(((x2-x1)*(y3-yl)-(x3-x1)*(y2-yl)));
    }
    int main()
    {
        int x1,x2,x3,yl,y2,y3;
        cin>>x1>>yl>>x2>>y2>>x3>>y3;
        int s=gets(x1,x2,x3,yl,y2,y3);
        cout<<"总面积:"<<s<<"   1/3面积:"<<1.0*s/3<<endl;
        double py=1.0*s/6/(x2-x3);
        cout<<"重心Py:"<<py<<endl;
        int tots=0;
        int num=0;
        for(int i=0;py<=y3;i++)
        {
            py+=1;
            num++;
            int ss=gets(x1,x2,(x1+x2)/2,yl,y2,py);
            tots+=ss;
            cout<<"上移"<<i<<"个:S="<<ss<<"    面积和:"<<tots<<endl;
        }
        double p=1.0*tots/(1.0*num)/(1.0*s);
        cout<<p<<endl;
        cout<<"倍数:"<<p*36<<endl;
    }
    View Code

    AC代码

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    ll x1,yl,x2,y2,x3,y3;
    ll s;
    int main()
    {
        while(cin>>x1)
        {
            cin>>yl>>x2>>y2>>x3>>y3;
            s=((x2-x1)*(y3-yl)-(x3-x1)*(y2-yl));
            cout<<abs(11*s)<<endl;
        }
    }
    View Code

    H.XOR(线性基)

    •参考资料

      [1]:【2019牛客多校第一场】XOR

    •题意

      有一个集合 S,里边有 n 个正整数($a_1,a_2,cdots ,a_n$);

      对于 S 的某子集 s',如果 s' 中所有元素的异或和为 0,就将 |s'| 加入到答案中;

      求解所有的异或为 0 的子集的元素个数之和($ans=sum |s'|$);

      注意,集合 ${2,2}$ 的可构成两个元素为 2 的子集 ${2_1},{2_2}$;

      也就是说子集是否相同和下标有关而与子集的元素是否相同无关;

    •思路

    把求每个异或和为0的子集长度和转化为求每个$a_{i}$异或和为0的贡献

    为什么可以这样转化呢?

    例如 在所有异或和为0的子集里,$x$个有$a_{1}$,$y$个有$a_{2},...$

    那么,$a_{1}$的贡献为$x$,$a_{2}$的贡献为$y,...$

    如何算贡献呢?可以根据基底!分为在基底中的数和不在基底中的数

    ①不在基底中的数 $cnt dot 2^{cnt-1}$种

    不在基底中的数,为什么不在基底中呢?因为加入后就线性相关,从而会产生0

    得到所有不在基底中的数$cnt$,从这$cnt$个数中拿出$1$与在基底中的组合有$cnt$中

    其他$cnt-1$个不在基底中的数,加入组合和不加入组合有$2^{cnt-1}$种,所有此种情况有$cnt dot 2^{cnt-1}$种

    ②在基底中的数 

    对于在基底中的数$a_{i}$,可能可以用其他数来代替,也可能不能被代替

    把除$a_{i}$以外的所有数组成基底 (假设有$y$个对基底没有作用)

    ①不能被代替

    也就是$a_{i}$与这个基底线性无关,可以插入到这个基底中,此时不能形成0

    ②可以被代替

    也就是$a_{i}$与这个基底线性有关,不可以插入到这个基底中,此时可以形成0

    那可以形成多少也就是贡献是多少呢?$2^{y}$

    因为此时$a_{i}$和基底可以形成一个0,还有$y$个数加入组合和不加入组合形成$2^{y}$种

    那$y$如何计算?可以得到所有不在基底中的数$cnt$,其中包含$a_{i}$,所以$y=cnt-1$

    •代码

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define mem(a,b) memset(a,b,sizeof(a))
      4 #define ll long long
      5 const int maxn=1e5+50;
      6 const int MOD=1e9+7;
      7   
      8 int n;
      9 ll a[maxn];
     10 ll base[70];
     11 ll L[maxn][70];///L[i]:前i个数(1~i)构成的基底
     12 ll R[maxn][70];///R[i]:后n-i+1个数(i~n)构成的基底
     13 bool vis[maxn];///vis[i]:判断a[i]是否在这n个数构成的基底中
     14   
     15 bool Insert(ll x)
     16 {
     17     for(int i=60;i >= 0;--i)
     18     {
     19         if(x>>i&1)
     20         {
     21             if(!base[i])
     22             {
     23                 base[i]=x;
     24                 return true;
     25             }
     26             x ^= base[i];
     27         }
     28     }
     29     return false;
     30 }
     31 ll qPow(ll a,ll b,ll mod)
     32 {
     33     ll ans=1;
     34     a %= mod;
     35     while(b)
     36     {
     37         if(b&1)
     38             ans=ans*a%mod;
     39         a=a*a%mod;
     40         b >>= 1;
     41     }
     42     return ans;
     43 }
     44 ll Solve()
     45 {
     46     mem(base,0);
     47     for(int i=1;i <= n;++i)
     48     {
     49         vis[i]=Insert(a[i]);
     50         memcpy(L[i],base,sizeof(base));
     51     }
     52       
     53     mem(base,0);
     54     mem(R[n+1],0);
     55     for(int i=n;i >= 1;--i)
     56     {
     57         Insert(a[i]);
     58         memcpy(R[i],base,sizeof(base));
     59     }
     60   
     61     int cnt=n;
     62     for(int i=0;i <= 60;++i)
     63         if(base[i])
     64             cnt--;
     65     ///计算出不在基底中的数对答案的贡献
     66     ll ans=cnt*qPow(2,cnt-1,MOD);
     67   
     68     for(int i=1;i <= n;++i)
     69     {
     70         if(!vis[i])
     71             continue;
     72   
     73         ///base:除a[i]外的其他n-1个数构成的基底
     74         memcpy(base,L[i-1],sizeof(L[i-1]));
     75         for(int j=0;j <= 60;++j)
     76             if(R[i+1][j])
     77                 Insert(R[i+1][j]);
     78   
     79         if(Insert(a[i]))///判断a[i]是否还可插入
     80             continue;
     81   
     82         cnt=n;
     83         for(int j=0;j <= 60;++j)
     84             if(base[j])
     85                 cnt--;
     86         ///a[i]对答案的贡献
     87         ans += qPow(2,cnt-1,MOD);
     88         ans %= MOD;
     89     }
     90     return ans;
     91 }
     92 int main()
     93 {
     94     while(~scanf("%d",&n))
     95     {
     96         for(int i=1;i <= n;++i)
     97             scanf("%lld",a+i);
     98   
     99         printf("%lld
    ",Solve()%MOD);
    100     }
    101     return 0;
    102 }
    View Code
  • 相关阅读:
    Binary Tree Inorder Traversal
    Populating Next Right Pointers in Each Node
    Minimum Depth of Binary Tree
    Majority Element
    Excel Sheet Column Number
    Reverse Bits
    Happy Number
    House Robber
    Remove Linked List Elements
    Contains Duplicate
  • 原文地址:https://www.cnblogs.com/MMMinoz/p/11209898.html
Copyright © 2011-2022 走看看