zoukankan      html  css  js  c++  java
  • 「2019.7.18 考试」关于压力下的加速度。

    今天早上又考了一场。

    现在题还没改完,先写一下考试路程。

    我习惯性的先看完了三道题。

    然后昨天教练还读了一下那个IOI选手的答题思路,就是先切第三题然后攻T2,我的话打算先把T3打个暴力就不管他,跑到考试结束连个6也没跑出来于是结束了。

    然后我就开始写T1的暴力了。

    看出来是欧拉路,但是我不会统计,然而用map判重一个傻逼重载运算符我卡了半天写不对,最后还是用了随机化hash的思想rand出了几百个数,挨个和边经过次数乘起来作为hash值这样的,然后判重过了,这道题就弃了开始干T2。

    T2我的思路真的是一点一点来的。

    T2是这次比较成功的一部分,虽然分数不是很理想(卡常不到位),我和天皇的暴力基本一样,但是他加了个clock然后70了。

    说一下暴力的思路,我的期望得分是(80),但是这个数据稍微有点苟,大于1e5的数据全都是1e9的,导致我全都MLE。

    1.首先狗掉了k==1和k==0的思路,k==0的话就直接GCD即可,k==1也同样是GCD,只需要枚举不同的集合GCD,然后判断不包含的那个是不是剪掉之后小于1,之后选出来最大的一个GCD即可。

    2.在观察数据范围,1e5范围的值域其实可以暴力切掉,1e5*100暴力判断即可,到这里是40。

    3.在接着qj不了数据范围了。

    但是我找到了一个性质:如果一个数d不符合要求,那么他的倍数也不符合要求,这样我们就可以打个埃筛,筛掉很多后面的数。

    复杂度变成了一个loglogn的,虽然还要判断,不过只需要判断质数也就是ln(n)的大小,虽然还是要循环O(n),但是判断只需要判断质数是否成立即可,因为前面的全部被他的最小质因子筛掉了,线筛会更优秀一些,基本接近线性了,所以我估分到了80,虽然是50,但是收获还是蛮大的。

    正解是数论分块,学反演的时候学过一次,同样和筛是一起学的,不过我对筛的认识还是比分快清晰一些,这样考场上打了筛而不是分块。

     

    好了现在可以写一下题解了。

     

    第一题的话,基本没怎么想正解,不过欧拉路那个的确想到了,没接着想,打了个暴力就过了,这个决定挺正确的,因为我后来看了正解之后,也想了好久才写了30分。

    首先可以把每条边全都分为正反向两条边,这样每个点的度数都是偶数了,这些有向边的话随便去除两条就可以达到效果,即其中两条是只走一次。

    那么剩下来的那些边必然也要保证是一条欧拉回路,也就是说,删的边有所要求,符合要求方案就是ans。

    三种情况:任意两个自环被删掉,任意自环和一条普通边被删掉(从普通边出发即可),两条有共同端点的边(保证奇点个数为偶)。

    这样计算一次出答案,特判掉虫洞不联通的情况。

     

    第二题,暴力的思路基本被我挖干净了,直接正解就行了。

    数论分块的一个小板子,推一下式子吧。

    $ sum limits_{i=1}^n (left lceil frac{a_i}{d} ight ceil d-a_i) leq k $

    $ d sum limits_{i=1}^n left lceil frac{a_i}{d} ight ceil leq k+sum limits_{i=1}^n a_i $

    $ sum limits_{i=1}^n left lceil frac{a_i}{d} ight ceil leqslant left lfloor frac{k+ sum limits_{i=1}^n a_i }{d} ight floor $

    发现右边是个数论分块的裸板子,可以$ O(sqrt{n}) $处理。

    那么来证明一下数论分块的正确性(之前听学长讲反演的时候听过),但是一直没有试着自己证明一下来的。

    现在来试一试。

    结论是:满足 $ frac{n}{last} = frac{n}{i} $ 的最大的last的值是 $ left lfloor frac{n}{left lfloor frac{n}{i} ight floor } ight floor $

    那么证明:

    $ last=i+d $

    $ ki+p=n $

    $ k(i+d) + p'= n $

    $ p' = p-kd $

    $ d_{max} = left lfloor frac{(p-p')}{k} ight floor = left lfloor frac{p}{k} ight floor $

    $ last = i+d =i+ left lfloor frac{p}{k} ight floor $

    $ last = i+d =i+ left lfloor frac{n- left lfloor frac{n}{i} ight floor i }{ left lfloor frac{n}{i} ight floor } ight floor $

    $ last = i+d = left lfloor i+ frac{n- left lfloor frac{n}{i} ight floor i }{ left lfloor frac{n}{i} ight floor } ight floor $

    $ last = i+d = left lfloor frac{n}{ left lfloor frac{n}{i} ight floor } ight floor $

    得证了。

    那么这题可以分块根号n解决了。

    还有一个优化。

    我们思考一下d的上界,如果d减去最小的 $ a_i $ 大于k,显然不成立,那么分块上界可以缩小到了 $ d_{min}=a_{min}+k $这样这题基本挖光了。

    下面是第三题,一个神仙dp。

    dp本身没什么说的,神就是神,思路也神,设的也神,sdfz的神犇全都打了正解也神。

    虽然本身神的我无话可说但是还是有一些可以吸收的营养。

    另num=dp[i-1][l]*dp[i-1][r]

    分情况:

    1.什么都不做:dp[i][l+r]+=num

    2.新根作一条:dp[i][l+r+1]+=num

    3.根和两边的连成一条:dp[i][l+r-1]+=2*num*l*r

    4.根和两边的某一条连:dp[i][l+r]+=2*num*(l+r)

    5.根和两边(同一侧)的两条连起来:dp[i][l+r-1]+=num*(l*(l-1)+r*(r-1))

    下面说一下我的理解。

    首先dp是一种设计。你需要用你的决策方程来维护你设计的dp状态,使得终阶段的答案状态是正确,满足要求的。

    那么看一下这五个方程分别满足了原题的什么性质。

    1.满足继承性,我的子树里所拥有的方案我也要拥有。

    2.满足扩展性,新来的根要扩展出。

    3.满足融合性,我们考虑把新根融入上一阶段的状态和决策中。

    4.满足有序性,我正着反着连到同一条边上,是不同的方案。

    5.满足新增性,也就是说多了我这一个点,上一阶段的某一些状态也会在这一阶段作出改变。

    满足这些性质,这个dp虽然神的要死。

    但终归是令人惊叹但正确无误的。

    关于题目的加速度。

    前几天的时候刷题的速度有点快,我自己也感觉到不太对劲了。但是我觉得对面那个D队的做题实在太快了,我不敢松懈,我觉得在自家OJ让人虐让人打脸我很不爽,我真不爽。

    然后就刻意提高速度,说实话这个过程很难受,感觉就是强行加速一样,那几天身体状况都不太好了起来,我觉得可能我那时候多半心态有一些问题。刻意提速是有问题的,当时可能是很受打击之类反正有一种情绪在里面,情绪这个东西控制不好就会炸。当时实在是心急了,现在想想教练说的挺对的,自己不要被别人的速度所干扰,不要因为别人比你强就是强行追赶他,这样下去你的速度被干扰了,最后肯定会垮掉,如果真的你用自己的速度到最后赶不上他那就认了就行了,因为你尽了全力还是不如他,就承认不如他就可以了,这也没什么丢人的。说的挺有道理的。

    压力这个是必须要有的,但是加速度必须是恒定的,也就是说你的进步速度必须是恒定的,不被外界干扰,不被情绪干扰,可以有情绪,但不能被他干扰,这很重要,这样会进步,而且会很快很稳。

    不要贪大贪多,能力多少就一次吃多少,不图快,图稳,扎实,这样后期一定会进步飞快。

    什么不行就要练什么,现在对于那个第一机房还是第二机房我觉得应该淡然一点,如果你的考试能力足够了,就去学知识,如果不够的话,那么就去考试就行了,两方面都很重要,教练分给你的一定是当前最适合你的一种训练方式。

    sdfz那边的确很强,这会让很多人有压力,学长那届也比我们强,但是最后我们不一定就会差。

    竞赛这个东西,毕竟不学到最后,是不会知道结果的。

    那今天就这么多。

    //T1
    #include<iostream>
    #include<cstdio>
    using namespace std;
    typedef long long ll;
    const int maxn=1e5+5;
    int n,m,x,y,tot,cnt,first[maxn],vis[maxn],d[maxn];
    ll ans,sum;
    struct Road{
        int u,t,nxt;
    }eage[maxn<<1];
    void add(int x,int y)
    {
        eage[++tot].u=x;
        eage[tot].t=y;
        eage[tot].nxt=first[x];
        first[x]=tot;
    }
    void dfs(int x)
    {
        vis[x]=1;
        for(int i=first[x];i;i=eage[i].nxt,cnt++)
            if(vis[eage[i].t]==0)
                dfs(eage[i].t);
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            add(x,y);add(y,x);
            if(x==y) sum++;
            if(x!=y) d[x]++,d[y]++;
        }
        for(int i=1;i<=n;i++) 
            if(d[i]) 
            {
                dfs(i);
                break;
            }
        if(cnt<m*2)
        {
            puts("0");
            return 0;
        }
        for(int i=1;i<=n;i++) ans+=1LL*d[i]*(d[i]-1)/2;
        ans+=1LL*(m-sum)*sum;
        ans+=1LL*sum*(sum-1)/2;
        printf("%lld
    ",ans);
        return 0;
    }
    //T2
    #include<iostream>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    const int maxn=105;
    ll ans,res,sum,n,k,a[maxn];
    int main()
    {
        scanf("%lld%lld",&n,&k);
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
            sum+=a[i];
        }
        sum+=k;
        for(ll i=1,pre;i<=sum;i=pre+1) 
        {
            pre=sum/(sum/i);res=0;
            for(int j=1;j<=n;j++) 
            {
                ll d=(a[j]-1)/pre+1;
                res+=pre*d;
            }
            if(res<=sum) ans=pre;
        }
        printf("%lld
    ",ans);
        return 0;
    }
    
    
    //T3
    #include<iostream> #include<cstdio> using namespace std; typedef long long ll; const int maxn=305; ll ans,n,m,dp[maxn][maxn]; int main() { scanf("%lld%lld",&n,&m); dp[1][0]=dp[1][1]=1; const int mod=m; for(register int i=1;i<=n-1;++i) { int t=(i>=20?n-i+1:min((ll)(1<<i)-1,n-i+1)); for(register int l=0;l<=t;++l) for(register int r=0;r<=t&&l+r-1<=n-i+1;++r) { ll sum=dp[i][l]*dp[i][r]%mod; dp[i+1][l+r]=(dp[i+1][l+r]+sum)%mod; dp[i+1][l+r+1]=(dp[i+1][l+r+1]+sum)%mod; dp[i+1][l+r]=(dp[i+1][l+r]+2*sum*(l+r)%mod)%mod; dp[i+1][l+r-1]=(dp[i+1][l+r-1]+2*sum*l%mod*r%mod)%mod; if(l==0&&r==0) continue; if(l==0&&r!=0) dp[i+1][l+r-1]=(dp[i+1][l+r-1]+sum*r*(r-1)%mod)%mod; if(l!=0&&r==0) dp[i+1][l+r-1]=(dp[i+1][l+r-1]+sum*l*(l-1)%mod)%mod; if(l!=0&&r!=0) dp[i+1][l+r-1]=(dp[i+1][l+r-1]+sum*((l*(l-1)+r*(r-1)))%mod)%mod; } } printf("%lld ",dp[n][1]%mod); return 0; }

    i=0i=0N

  • 相关阅读:
    学习java集合LinkedHastSet
    学习java,equals方法
    学习java,入门语言java的感概
    学习java哈希值,java入门编程语言
    学习java集合HashSet
    学习java集合set集合
    学习java集合LinkedList
    学习java集合Arraylist
    学习java集合list集合
    学习Java数据结构(入门选Java)
  • 原文地址:https://www.cnblogs.com/Lrefrain/p/11206408.html
Copyright © 2011-2022 走看看