• 2020牛客寒假算法基础集训营2


    感觉题质量挺好的,能学到东西或者完善技能树。

    A了8题,实际上只有7题,有一题队友抬的,dp还是较弱项感觉。

    这场感觉数学和思维偏重,开场签了A,B后就停滞了,挂机了一个多小时,还以为要2题结束了。

    后来手推反倒是过了当时过的人还不多的E,信心又回来了,状态就上来啦。

    顺序是ABEGDFCH,密集过题是1.5~3.5小时过了5题。前10分钟过了两题,(盲目)分析下似乎只要在线2个小时的高效状态就比较不错了。

    记录一下思维。赛中没过的贴个代码。

    按顺序来吧。

    E.做计数

    给的式子平方之后,就是要找sqrt(i*j)是整数的组合,记i*j=t 枚举sqrt(t),找t*t的因子即可。

    话说我怎么看到这种多少对i,j,k就想到fft了。。。。。最近是不是做fft太多了。想了几分钟fft就排除了。。。

    G.判正误

    开场10分钟之后看到自闭的题,因为显然取模运算之后的数算出来的答案和原来不一样,也就是令人困惑的点。

    但实际上仔细想想,YES的条件十分的苛刻,一旦YES,等式成立,那么这个等式按照任意质数模数算出来都应该同样是成立的。

    所以对多个模数快速幂算一下即可,如果都成立,则YES,否则NO。

    出题人卡了1e9+7,因为这个实在太常用了,似乎有随便换一个较大的质数就过的代码,实际上很不保险,知道你的模数,可以卡你的。

    pow都可以过,有点晕。

    D.数三角

    看到题,n=500,算了下C(n,3)发现正好2e7左右,于是就开始写暴力,1s能过应该不是问题。。

    写了暴力,就判钝角,然后忘了判共线,组不成三角形的情况,WA了一发就过了。

    赛后补了n2log的做法,实际上n可以出到更大,建议参考HDU5784。

    F.拿物品

    交了5发错误的贪心策略,有先拿差值最大,先拿各自值最大等等策略的贪心。

    最后冷静下来玩了临界情况的样例,当对方能得到很大的值,你是阻止对方拿?还是管自己拿大?然后就出来了,是拿a+b最大。

    题解给的实际上非常有说服力。建议参考题解的证明。

    C.算概率

    开场自闭题2,最原始想法是组合数枚举嘛,就是你n个题做对k个,有C(n,k)的选法,但这样肯定做不了。

    灵光乍现,或者参考群友提到的概率dp(虽然我没做过概率dp...)

    想到dp,然后你就会了。

    dp[i][j]记录 i 道题做对 j 道的概率。

    然后转移很自然,就是  dp[i-1][j]*这道题做错 + dp[i-1][j-1]*这道题做对的概率。

    二维dp的思路比较清晰。

    然后实际上可以优化掉第二维。。。就是因为每次只与前一次有关,第二重循环反向枚举,滚动掉第二维?背包?(学了下,但还是不太会)  但复杂度n^2跑不了,数组开二维问题也不大。

    (这就体现了dp弱项了....)

    H.施魔法

    就是排完序后 找 每段长度>=k的不重叠子段  使得总cost最小,cost为两个端点的差值。

    队友抬的题,这题我比赛里也只会n^2的dp,

    比赛时交了一发TLE的n^2的dp...实际上了就可以从式子上优化到O(n).

    我是这么写的

            for(int i=k;i<=n;++i)

            {
                for(int j=0;j<=i-k;++j)
                {
                    dp[i]=min(dp[i],dp[j]+a[i]-a[j+1]);
                }
     
            }

     其实是跟题解里的式子一样的,第一个等号出来了,关键在于第二个等号,我们发现对于第二个循环枚举j来说,a[i]是多余的,可以提出来,然后剩下dp[j]-a[j+1]。

    其实我们枚举第二个循环的目的就找这个最小的dp[j]-a[j+1],但这个式子只与当前的自变量有关,我们可以直接用一个pre变量在算的同时维护掉,省去这个第二重枚举。

    就是dp递推转移式子的化简来实现dp复杂度的优化。

    赛后给的题解里还是能学到挺多的感觉。

     1 #include <bits/stdc++.h>
     2 #ifndef ONLINE_JUDGE
     3 #define debug(x) cout << #x << ": " << x << endl
     4 #else
     5 #define debug(x)
     6 #endif
     7 using namespace std;
     8 typedef long long ll;
     9 const int MAXN=3e5+7;
    10 const int INF=0x3f3f3f3f;
    11 const int MOD=1e9+7;
    12  
    13 int dp[MAXN];
    14 int a[MAXN];
    15  
    16 int main()
    17 {
    18     ios::sync_with_stdio(false);
    19     cin.tie(0);
    20     int n,k;
    21     cin>>n>>k;
    22     for(int i=1;i<=n;++i) cin>>a[i];
    23     if(k==1) cout<<0<<endl;
    24     else
    25     {
    26         memset(dp,0x3f,sizeof(dp));
    27         sort(a+1,a+n+1);
    28         int pre=-a[1];
    29         for(int i=k;i<=n;++i)
    30         {
    31             dp[i]=pre+a[i];
    32             pre=min(pre,dp[i-k+1]-a[i-k+2]);
    33         }
    34         cout<<dp[n]<<endl;
    35     }
    36     return 0;
    37 }

    I.建通道

    比赛里感觉思路大致方向对了,但没想好连边方式。导致想不好...

    就是求个特殊位运算的最小生成树。

    显然相同的v值之间无需建边,cost=0,就排序去重一下即可。剩下的点m个;

    我们还需要在剩下m个点连m-1条边。注意此处的特判,别判n-1,而是去重后的点数-1。

    然后找到最小的二进制位有0,有1,答案就是(m-1)*这位权值,为啥,因为所有的当前位这位二进制为1的点都可以和为0的这个点连,反之亦然。然后写了个O(30n)的写法。

    出题人的写法统计二进位01存在性统计 用 & 和 | 扫一遍就更巧妙啦。学到了

     1 #include <bits/stdc++.h>
     2 #ifndef ONLINE_JUDGE
     3 #define debug(x) cout << #x << ": " << x << endl
     4 #else
     5 #define debug(x)
     6 #endif
     7 using namespace std;
     8 typedef long long ll;
     9 const int MAXN=2e5+7;
    10 const int INF=0x3f3f3f3f;
    11 const int MOD=1e9+7;
    12  
    13 int v[MAXN];
    14  
    15 int main()
    16 {
    17     ios::sync_with_stdio(false);
    18     cin.tie(0);
    19     int n;
    20     cin>>n;
    21     for(int i=0;i<n;++i)cin>>v[i];
    22     sort(v,v+n);
    23     ll m=unique(v,v+n)-v;
    24     if(m==1) cout<<0<<endl;
    25     else
    26     {
    27  
    28         for(int i=0;i<=30;++i)
    29         {
    30             int cnt0=0,cnt1=0;
    31             for(int j=0;j<m;++j)
    32             {
    33                 if((v[j]&(1ll<<i))==0) cnt0++;
    34                 else cnt1++;
    35             }
    36             if(cnt0&&cnt1)
    37             {
    38                 cout<<1ll*(1ll<<i)*(m-1)<<endl;
    39                 break;
    40             }
    41         }
    42     }
    43     return 0;
    44 }

    J.求函数

    又是道可以学习的题。这种题之前还没遇到过。代入一下发现,就是    (((k(k(k(k*1+b)+b)+b)+b))) 这样的嵌套式子,拆开来就是题解给的那个式子。

     然后明确这个式子的区间合并方式,(这题的灵魂)就过了。

    左边那个显然直接乘就好。

    右边那个,.....好吧我讲不清,建议看题解,反正数学式子感觉到了就行...(逃~

    https://ac.nowcoder.com/discuss/364961?type=101&order=0&pos=6&page=3

    然后线段树分别维护这两个值就行,区间合并函数写好,或者重载一个运算符就行。

     1 #include <bits/stdc++.h>
     2 #ifndef ONLINE_JUDGE
     3 #define debug(x) cout << #x << ": " << x << endl
     4 #else
     5 #define debug(x)
     6 #endif
     7 #define lson (o<<1)
     8 #define rson (o<<1|1)
     9 using namespace std;
    10 typedef long long ll;
    11 const int maxn=2e5+7;
    12 const int INF=0x3f3f3f3f;
    13 const int MOD=1e9+7;
    14 
    15 struct node
    16 {
    17     ll k,b;
    18     node(ll x=0,ll y=0):k(x),b(y){}
    19     friend node operator + (const node &x,const node &y)
    20     {
    21         return {x.k*y.k%MOD,(x.b*y.k+y.b)%MOD};
    22     }
    23 }t[maxn<<2];
    24 ll k[maxn],b[maxn];
    25 void build(int o,int l,int r)
    26 {
    27     if(l==r)
    28     {
    29         t[o]={k[l],b[l]};
    30         return ;
    31     }
    32     int mid=l+r>>1;
    33     build(lson,l,mid);
    34     build(rson,mid+1,r);
    35     t[o]=t[lson]+t[rson];
    36 }
    37 
    38 void update(int o,int l,int r,int pos,int k,int b)
    39 {
    40     if(l==r)
    41     {
    42         t[o]={k,b};
    43         return ;
    44     }
    45     int mid=l+r>>1;
    46     if(pos<=mid) update(lson,l,mid,pos,k,b);
    47     else update(rson,mid+1,r,pos,k,b);
    48     t[o]=t[lson]+t[rson];
    49 }
    50 node query(int o,int l,int r,int ql,int qr)
    51 {
    52     if(ql<=l && qr>=r) return t[o];
    53     int mid=l+r>>1;
    54     if(qr<=mid) return query(lson,l,mid,ql,qr);
    55     if(ql>mid) return query(rson,mid+1,r,ql,qr);
    56     return query(lson,l,mid,ql,qr)+query(rson,mid+1,r,ql,qr);
    57 }
    58 
    59 int main()
    60 {
    61     int n,m;
    62     scanf("%d%d",&n,&m);
    63     for(int i=1;i<=n;++i) scanf("%lld",&k[i]);
    64     for(int i=1;i<=n;++i) scanf("%lld",&b[i]);
    65     build(1,1,n);
    66     for(int i=0,op,l,r;i<m;++i)
    67     {
    68         scanf("%d",&op);
    69         if(op==1)
    70         {
    71             int pos,x,y;
    72             scanf("%d%d%d",&pos,&x,&y);
    73             update(1,1,n,pos,x,y);
    74         }
    75         else
    76         {
    77             scanf("%d%d",&l,&r);
    78             node res=query(1,1,n,l,r);
    79             printf("%lld
    ",(res.k+res.b)%MOD);
    80         }
    81     }
    82     return 0;
    83 }

    结束啦,逃...

  • 相关阅读:
    Shell编程基础
    lenovo future leaer deveolpmetn program
    求1+2+...+n
    Linux下使用qq
    判断2个线段是否相交
    java大数相加
    Django路由系统
    Django框架
    HTTP协议及Django配置
    mysql索引
  • 原文地址:https://www.cnblogs.com/Zzqf/p/12273756.html
Copyright © 2020-2023  润新知