2019牛客暑期多校训练营(第一场)补题记录

总结

总结放前面方便之后看自己总体上做的不好的地方,以便改正。

因为在实习,没有那么快开题,一进去后看到J题过的人多,就去做J,结果花了足足1小时才过。之后一直在肝B,以为是一道找规律加递归版逆元公式套过,结果没解出来。然后想套个simpson自适应来找规律,还是莫得办法。到第三个小时心态有点崩,看了概率的F,没积出来,靠,心态崩了。
总的来说,这次应该是提醒下次要全程状态在线,同时一道题如果快半个小时也没找到思路就要先去做其他的题目,以便及时找到自己擅长的领域(线段树,数论以及基础dp)。同时数论不能只学知识点,虽然FFT、母函数、以及基本的涉及素数的全部算法都学懂了(详见KineXence的数论博客),但做的题目少,导致这次遇到数学题还是没有自信去开。不仅要去学数学知识,还要结合算法题目,这是这次比赛的教训。(另外看完题目再去思考,往往只看到中叶的思路跟题目的解法会有出入)
话不多说,开始补题:

A:Equivalent Prefixes

分析题。首先假设长度为len的区间符合条件(区间设为ABC),那么len++的时候,新增的区间为D,CD,BCD,ABCD,判断这些区间是否符合条件。所以设一个j-1,往回跑判断即可。当a[D]>a[j]&&b[D]>b[j]时,D在新增区间AB…j中一定不是最小的,那么直接跳出循环。
代码:

#include<bits/stdc++.h>
using namespace std;

const int MAX_N = 1e5 + 5;

int a[MAX_N],b[MAX_N];

int main()
{
    int n;
    while(scanf("%d",&n)==1){
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int i=1;i<=n;i++) scanf("%d",&b[i]);
        bool flag = 1;
        int i;
        for(i=1;i<=n;i++){
            for(int j=i-1;j>0;j--){
                if(a[i]>a[j]&&b[i]>b[j]) break;
                else if((a[i]>a[j]&&b[i]<b[j])||(a[i]<a[j]&&b[i]>b[j])){
                    flag = 0;
                    break;
                }   //最小值的下标改变的点不同
                else {} //表示最小值下标都改到了i的位置,符合条件,但得继续判断完剩余区间
            }
            if(flag==0) break;
        }
        printf("%d\n",i-1);
    }
    return 0;
}

B:Integration

自己sb题。二十分钟没想出来就得立即换,以免没时间做自己会做的题目。
除了是自己sb,这套题还是个数学题。这个题目给了我提醒,像这种数学构造题目一般如果FFT和母函数不能解决的,那么一般不可能可以用规律推出。那么我们得根据题目所给的条件,将目标函数转化成题目所给的条件的形式。
只有累加才能拆开积分,那么我们假设被积分的函数 1 ∏ ( a i 2 + x 2 ) = c 1 ( a 1 2 + x 2 ) + c 2 ( a 2 2 + x 2 ) + . . . c n ( a n 2 + x 2 ) \frac{1}{\prod(a_i^2+x^2)} =\frac{c_1}{(a_1^2+x^2)}+\frac{c_2}{(a_2^2+x^2)}+...\frac{c_n}{(a_n^2+x^2)} (ai2+x2)1=(a12+x2)c1+(a22+x2)c2+...(an2+x2)cn,写到这步应该知道要求出 c i c_i ci的值来对其构造了。记住,在数学等式中引入虚数是没问题的,只要等式成立。先求 c 1 c_1 c1,等式两边相乘 ( a 1 2 + x 2 ) (a_1^2+x^2) (a12+x2),发现令 x 2 = − a 1 2 x^2 = -a_1^2 x2=a12可以消掉其他的系数。那么就有 c 1 = 1 ∏ k = 1 a n d k ! = i n ( a k 2 − a i 2 ) c_1 = \frac{1}{\prod_{k=1andk!=i}^{n}(a_k^2-a_i^2)} c1=k=1andk!=in(ak2ai2)1,是个 O ( n 2 ) O(n^2) O(n2) 的算法。题目提示 n 2 &lt; 1 0 7 n^2&lt;10^7 n2<107,复杂度可以接受。那么我们的式子就转化为: 1 π ∫ 1 ∏ ( a i 2 + x 2 ) d x = ∑ 1 2 a i ∗ c i \frac{1}{\pi}\int{\frac{1}{\prod(a_i^2+x^2)}}dx=\sum{\frac{1}{2a_i}*c_i} π1(ai2+x2)1dx=2ai1ci
代码:

#include<bits/stdc++.h>
using namespace std;

const int MOD = 1e9 + 7;
typedef long long LL;

long long inv(long long a)
{
    if(a == 1)
        return 1;
    return (MOD-MOD/a)*inv(MOD%a)%MOD;
}

LL qkpow(LL a,LL p,LL mod)
{
    LL t=1,tt=a%mod;
    while(p)
    {
        if(p&1)t=t*tt%mod;
        tt=tt*tt%mod;
        p>>=1;
    }
    return t;
}

LL getInv(LL a,LL mod)
{
    return qkpow(a,mod-2,mod);
}

int main()
{
    int n;
    long long a[1005] = {0};
    while(scanf("%d",&n)==1){
        long long sum = 0;
        for(int i=1;i<=n;i++) scanf("%lld",&a[i]);

        for(int i=1;i<=n;i++){
            long long ind = 1LL;
            for(int k=1;k<=n;k++){
                if(k==i) continue;
                ind *= ((a[k]*a[k]%MOD - a[i]*a[i]%MOD)%MOD+MOD)%MOD,ind %= MOD;
            }
            ind = inv(ind) * inv(2)%MOD*inv(a[i])%MOD;
            sum += ind,sum %= MOD;
        }
        if(sum<0) sum+=MOD;
        printf("%lld\n",sum);
    }
    return 0;
}

这道题要注意,理论上只有1~MOD-1才有逆元值,所以在用递推逆元函数的时候要保证里面的参数严格小于MOD值(取模后再求逆元)。同时 i n v ( a 1 ) ∗ i n v ( a 2 ) ∗ . . . ∗ i n v ( a n ) = i n v ( a 1 ∗ a 2 ∗ . . . ∗ a n M O D p ) inv(a_1)*inv(a_2)*...*inv(a_n) = inv(a_1*a_2*...*a_nMODp) inv(a1)inv(a2)...inv(an)=inv(a1a2...anMODp),所以用后面的方法可以节省不少复杂度。

E:ABBA

dp我构建的模型不太多,这次学到了如何用dp来表示字符串是否合法。
我们对这道题进行分析,有n个AB和m个BA,那么一定有前n个A是属于AB的,同样有前m个B是属于BA的。对于合法解中有重叠子问题,那么我们用动态规划的思路来考虑。我们构建一个状态方程式 d p [ i ] [ j ] dp[i][j] dp[i][j]代表已选上i个A与j个B。
状态转移分析:当我们的i<n的时候,可以直接插入A,因为非A即B,要保证前n个B的前面至少有n个A。当我们i>=n的时候,我们的A不能超过目前还需匹配成“BA”的B的最大数目,即我们多出的A不能超过 m i n ( j , m ) min(j,m) min(j,m),表达式:当 i − n &lt; m i n ( j , m ) i-n&lt;min(j,m) in<min(j,m),我们还可以继续插入A。
同理B的判断也跟A相同。边界值: d p [ 0 ] [ 0 ] = 1 dp[0][0]=1 dp[0][0]=1,n=m=0有且仅有一个解。
废话不多说,上代码:

#include<bits/stdc++.h>
using namespace std;

const int MAX_N = 2e3 + 5;

int dp[MAX_N][MAX_N];

const int MOD = 1e9 + 7;

void init(int n,int m)
{
    int len = n + m;
    for(int i=0;i<=len;i++){
        for(int j=0;j<=len;j++){
            dp[i][j] = 0;
        }
    }
    dp[0][0] = 1;   //n=0,m=0有且仅有一解
}

int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)==2){
        init(n,m);
        int len = n+m;
        for(int i=0;i<=len;i++){
            for(int j=0;j<=len;j++){
                if(i<n||(i-n)<min(j,m)){
                    dp[i+1][j] += dp[i][j];
                    dp[i+1][j] %= MOD;
                }
                if(j<m||(j-m)<min(i,n)){
                    dp[i][j+1] += dp[i][j];
                    dp[i][j+1] %= MOD;
                }
                //printf("%d\n",dp[i][j]);
            }
        }
        printf("%d\n",dp[len][len]);
    }
    return 0;
}

int的值在2e9+1e8左右,所以不需要开long long。曾经有个学长开long long做题超时了,可以用int就不要用LL。

F:Random Point in Triangle

有一学长用等边三角形进行积分…说实话还真的很方便。设等边三角形的底长为L,那么我们可以通过二重积分写出关于L的式子: E = ∫ − 1 4 L 0 ∫ − 3 3 x − 3 12 L 3 x + 3 4 L L ∗ y + 3 8 L 2 E = \int_{-\frac{1}{4}L}^{0}\int_{-\frac{\sqrt{3}}{3}x-\frac{\sqrt{3}}{12}L}^{\sqrt{3}x+\frac{\sqrt{3}}{4}L}L*y+\frac{\sqrt{3}}{8}L^2 E=41L033 x123 L3 x+43 LLy+83 L2,积出来的结果与用 L = 4 3 S 3 L=\sqrt{\frac{4\sqrt{3}S}{3}} L=343 S 带入,然后根据三点坐标求出S,代入其中*36即可。结果是 11 / 2 ∗ S 11/2*S 11/2S
代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    ll x1,y1,x2,y2,x3,y3;
    while(scanf("%lld%lld%lld%lld%lld%lld",&x1,&y1,&x2,&y2,&x3,&y3)==6){
        ll a = x2 - x1, b = y2 - y1;
        ll c = x3 - x1, d = y3 - y1;
        ll ans = abs(a * d - b * c);
        printf("%lld\n",ans*11);
    }
    return 0;
}

J:Fraction Comparision

跑了LL和long double都没过,考虑叉乘,但是数据肯定会爆1e18。然鹅。。。看了RANK1的组,居然有一个__int128…网上搜了一下,输入输出流是没有重载__int128类型的,所以输入输出需要自己写,但它确实也能储存1e35的数字。但这道题输入最多LL型,而输出只需要输出字符,同时__int128支持比较,所以可以这么写…
代码1:

#include<bits/stdc++.h>
using namespace std;

typedef long long LL;

int main()
{
    int a,b;
    LL x,y;
    while(scanf("%lld%d%lld%d",&x,&a,&y,&b)==4){
        __int128 l = (__int128)x * b;
        __int128 r = (__int128)y * a;
        if(l>r){
            printf(">");
        }
        else if(l<r){
            printf("<");
        }
        else{
            printf("=");
        }
        printf("\n");
    }
    return 0;
}

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值