zoukankan      html  css  js  c++  java
  • noip错题集

    noip再刷题

     

    解方程 用了秦九昭算法

    f(x)=0,f(x%p)=0,缩小数据范围的思想,就像以前一到考对数的题,p最好取可靠的质数。

     

    天天爱跑步 树上差分,一次性统计

     

    运输计划 树上差分

     

    飞扬的小鸟  第一次做是完全背包和01背包顺序弄反,这一次做的时候更新有问题,只枚举了下界到上界的更新,在完全背包的时候必须从一开始,而不能从下界开始(思考为什么)

     

    蚯蚓 数学证明,两个优先队列,对于在变化的增量, 我们可以在入队时提前减去贡献,就不用考虑取出时的变化,最后的三个队列不能sort,复杂度会爆

     

    斗地主 只是一张或两张牌的情况不应该搜

     

    逛公园 最短路,计划化搜索,对于从终点还可以走回去的情况,新建一个最终原点

     

    宝藏 一道很好的状压

     

    花匠 我用的线段树dp,实际上可以贪心

     

    华容道 其实分的最短路还是不错了

    攻击装置

    部落战争 

    https://www.cnblogs.com/EdSheeran/p/8718577.html

    forgetten one:以上两道是二分图匹配,有一个经典模型最小路径覆盖;

    二分图最大匹配=最小点覆盖=总点数-最小点独立集;

    大致证明:

    最大匹配=最小点覆盖:如果还能匹配,那就证明还有边没被覆盖。

    最小点覆盖=n-最大点独立集:如果独立集可以增大k,那就证明有k个点间两两没边,那最小点覆盖就可以删去这k个点。N个点,去掉最小点覆盖的,剩下的两两没边,就是最大点独立集。

    最小边覆盖=n-最大匹配:本来是n条边(每个点要被一条边覆盖),每匹配一对,就相当于减少了一条边。

    最小路径覆盖:最坏情况是一个点一个覆盖,每覆盖一次路径数就减少1,尽可能匹配多的边,每匹配一次相当于融合一次点;

    相互攻击:找最小点覆盖,我们希望删去一些点,这些点攻击的人很多,又要使这些点尽量少,所以就是最小点覆盖;

    count 数字计数

    拆开考虑的数位DP,存了一个sum,表示s在当前数中出现的次数,到底就统计sum的贡献,加上这一维是因为他<=数位

    骑士

    画图得到经典性质:树+最多一个环,如果没有环就是裸上司的舞会;

    有环拆成树,强制不走换上的边,从两边各做一次强制祖先不选的舞会,此题还要开longlong,又忘了;

    安慰奶牛cheer 我可能是个傻逼,一道黄题想不出来;

    最小生成树:关键是把点权划归在边权上,边走两次,点会对每条边进行贡献;

    The Captain 寒假最后一测原题, dijistra的板子; 题意求:边权min(|xi-xj|, |y1-yj|),求起点到终点的最短路;

    https://www.cnblogs.com/EdSheeran/p/8496929.html

    Eat the Trees 轮廓线DP裸题;(不是思维,难在代码)

    每次轮廓线的代码是最难的,轮廓线设计的方式很重要,决定了代码的难度,这题的巧妙之处在于直接把轮廓线对应的位置进行翻转:

    #include<bits/stdc++.h>
    using namespace std;
    #define ex(i, u) for(int i = h[u]; i; i = G[i].nxt)
    #define ll long long
    const int M = (1<<12) + 5;
    ll dp[13][13][M];
    bool mp[13][13];
    int read(){
        int x=0,f=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c<='9'&&c>='0'){x=x*10+c-'0';c=getchar();}
        return x*=f;
    }
    
    
    int main(){
        //freopen("cc.out","w",stdout);
        int T, idc = 0;
        scanf("%d", &T);
        while(T--){
            int n, m;
            scanf("%d%d", &n, &m);
            memset(dp, 0, sizeof(dp));
            for(int i=1; i<=n; i++)
                for(int j=1; j<=m; j++) scanf("%d", &mp[i][j]);
            dp[0][m][0] = 1;
            for(int i = 1; i <= n; i++){
                
                /*for(int s = 0; s < 1<<(1+m); s++)
                    dp[i][0][s>>1] = dp[i-1][m][s]; */
                    for(int s = 0; s < (1<<m); s++)
                    dp[i][0][s<<1] = dp[i-1][m][s];
    
                
                for(int j = 1; j <= m; j++) 
                    for(int s = 0; s < 1<<(1+m); s++){
                        int p1 = 1<<(j-1), p2 = 1<<j;
                        if(mp[i][j]){
                            if((p1&s) && (p2&s)) dp[i][j][s] = dp[i][j-1][s^p1^p2];
                            else if(!(p1&s) && !(p2&s)) dp[i][j][s] = dp[i][j-1][s^p1^p2];
                            else dp[i][j][s] = dp[i][j-1][s] + dp[i][j-1][s^p1^p2];
                            
                        }
                        else{
                            if(!(p1&s) && !(p2&s)) dp[i][j][s] = dp[i][j-1][s];
                        }
                        //if(dp[i][j][s])printf("%d %d %d %lld
    ",i, j, s, dp[i][j][s]);
                    }
            }
            printf("Case %d: There are %I64d ways to eat the trees.
    ", ++idc, dp[n][m][0]);
        }
    }

    两双手 思维DP;

    排序+容斥

    为什么排序后不考虑从后面回来呢? 由于坐标化简后相当于只向右上走,所以按坐标排序,只有排在它之前的点可能到达它

    假面舞会: 一道探究性质的图论好题

    https://blog.csdn.net/sunshinezff/article/details/48628401

    两个难点:对环和链性质的分类讨论,建立-1的边保证找到环的起点和终点;

    绝世好题

    https://vjudge.net/contest/263626#problem/A

    给定一个长度为n的数列ai,求ai的子序列bi的最长长度,满足bi&bi-1!=0(2<=i<=len)。

    n<=100000,ai<=2*10^9

    题解:很明显是一个拆位建边的题,但这样肯定TT;

    可以直接DP,我上次没有深刻理解,这次理解了,但自己实现的时候还是思考不够深刻;

    单独一位直接往下扫就好了,但很多时候边都是交错的,我自以为解决了这个问题,其实不然;

    很简单的例子:2 3 1;

    把WA和AC的对比一下;

    WA

    #include<bits/stdc++.h>
    using namespace std;
    const int M = 1e5 + 5;
    struct node{int w, t;}q[M];
    int a[M], f[M];
    int main(){
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= n; i++)scanf("%d", &a[i]), f[i] = 1;
        int ans =  0;
        for(int i = 0; i <= 31; i++){
            int lst = 0;
            for(int j = 1; j <= n; j++){
                if(a[j] & (1LL<<i)){
                    f[j] = max(f[j], f[lst]+1);
                    if(f[j] > f[lst]) lst = j;    
                } 
            }
        }
        for(int i = 1; i <= n; i++) ans = max(ans, f[i]);
        printf("%d
    ", ans);
    }

    AC

    #include<bits/stdc++.h>
    using namespace std;
    int f[33];
    int main(){
        int n;
        scanf("%d", &n);
        for(int i = 1; i <= n; i++){
            int mx = 0, x;
            scanf("%d", &x);
            
            for(int i = 0; i <= 31; i++)
                if(x & (1<<i)) mx = max(mx, f[i]);
            for(int i = 0; i <= 31; i++)
                if(x & (1<<i))f[i] = max(f[i], mx + 1);
        }
        
        int ans = 0;
        for(int i = 0; i <= 31; i++) ans = max(ans, f[i]);
        printf("%d
    ", ans);
    }

    D - BLO

    这题是原题,是对tarjan点双联通的一个运行过程的理解,一道好题;

    可以参看以前写的:https://www.cnblogs.com/EdSheeran/p/9029346.html

    Check the difficulty of problems

    http://poj.org/problem?id=2151

    一道概率DP,以前写过,但现在一点思路都没有,概率期望问题很大,而且因为玄学问题WA了一上午; 

    题意:
    ACM比赛中,共M道题,T个队,pij表示第i队解出第j题的概率
    问 每队至少解出一题且冠军队至少解出N道题的概率。
    
    解析:DP
    设dp[i][j][k]表示第i个队在前j道题中解出k道的概率
    则:
    dp[i][j][k]=dp[i][j-1][k-1]*p[j][k]+dp[i][j-1][k]*(1-p[j][k]);
    先初始化算出dp[i][0][0]和dp[i][j][0];
    设s[i][k]表示第i队做出的题小于等于k的概率
    则s[i][k]=dp[i][M][0]+dp[i][M][1]+``````+dp[i][M][k];
    
    则每个队至少做出一道题概率为P1=(1-s[1][0])*(1-s[2][0])*```(1-s[T][0]);
    每个队做出的题数都在1~N-1的概率为P2=(s[1][N-1]-s[1][0])*(s[2][N-1]-s[2][0])*```(s[T][N-1]-s[T][0]);
    
    最后的答案就是P1-P2
    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    #include<iostream>
    #include<math.h>
    using namespace std;
    
    double dp[1010][50][50];
    double s[1010][50];
    double p[1010][50];
    int main()
    {
        int M,N,T;
        while(scanf("%d%d%d",&M,&T,&N)!=EOF)
        {
            if(M==0&&T==0&&N==0)break;
            for(int i=1;i<=T;i++)
              for(int j=1;j<=M;j++)
               scanf("%lf",&p[i][j]);
            for(int i=1;i<=T;i++)
            {
                dp[i][0][0]=1;
                for(int j=1;j<=M;j++)dp[i][j][0]=dp[i][j-1][0]*(1-p[i][j]);
    
                for(int j=1;j<=M;j++)
                  for(int k=1;k<=j;k++)
                    dp[i][j][k]=dp[i][j-1][k-1]*p[i][j]+dp[i][j-1][k]*(1-p[i][j]);
    
                s[i][0]=dp[i][M][0];
                for(int k=1;k<=M;k++)s[i][k]=s[i][k-1]+dp[i][M][k];
            }
            double P1=1;
            double P2=1;
            for(int i=1;i<=T;i++)
            {
                P1*=(1-s[i][0]);
                P2*=(s[i][N-1]-s[i][0]);
            }
            printf("%.3lf
    ",P1-P2);
        }
        return 0;
    }
    View Code
     
  • 相关阅读:
    【BZOJ3995】[SDOI2015]道路修建 线段树区间合并
    [Noip2016]天天爱跑步 LCA+DFS
    【BZOJ2870】最长道路tree 点分治+树状数组
    【BZOJ3730】震波 动态树分治+线段树
    【BZOJ2969】矩形粉刷 概率+容斥
    【BZOJ3029】守卫者的挑战 概率+背包
    【BZOJ3043】IncDec Sequence 乱搞
    【BZOJ3124】[Sdoi2013]直径 树形DP(不用结论)
    Django学习笔记之ORM多表操作
    SQL学习笔记之项目中常用的19条MySQL优化
  • 原文地址:https://www.cnblogs.com/EdSheeran/p/9799510.html
Copyright © 2011-2022 走看看