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
  • 相关阅读:
    模拟_大数字符串(HDU_2054)
    DP_字串匹配(HDU_1501)
    动态字典树_字串标记查找+大数(HDU_4099)
    动态字典树_字串查找匹配(HDU_1075)
    动态字典树+DFS(HDU_1298)
    动态字典树_拆分查找(HDU_1247)
    动态字典树_统计前缀子串(HDU_1251)
    动态字典树_统计子串(HDU_2846)
    字典树讲解
    HTML5语义标签的实践(blog页面)
  • 原文地址:https://www.cnblogs.com/MMMinoz/p/11209898.html
Copyright © 2011-2022 走看看