zoukankan      html  css  js  c++  java
  • [jzoj NOIP2018模拟10.29]

    OI生涯的最高分,来了纪中这么多天,在经历了这么多场“NOIP难度”的模拟赛之后,终于看到了真正的NOIP

    今天考场上效率很高,很快码完了全部的题目,留下了足够的时间对拍和...发呆。不得不说看着电脑页面上看着三个bat心里还是很有成就感的,对拍过的代码自然而然分就高了

    没有犯什么小错误,数组越界变量写错什么的都没有,只有T3代码出现了一丝纰漏但也通过对拍查到了错。这告诉我们,对拍是很重要的,与其去追求没有把握的正解不如踏踏实实拿到可以拿的分数


    T1:列队

    题目链接:

    https://jzoj.net/senior/#contest/show/2543/0 

    题目:

         Sylvia是一个热爱学习的女孩子。
         在平时的练习中,他总是能考到std以上的成绩,前段时间,他参加了一场练习赛,众所周知,机房是一个 的方阵。这天,他又打爆了std,感到十分无聊,便想要hack机房内同学的程序,他会挑选一整行或一整列的同学进行hack ( 而且每行每列只会hack一次 ),然而有些同学不是那么好惹,如果你hack了他两次,他会私下寻求解决,Sylvia十分害怕,只会hack他们一次。假设Sylvia的水平十分高超,每次hack都能成功,求他最 多能hack多少次?

    题解:

    发现特殊的同学要么从所在行被hack,要么从所在列被hack

    每一行看成一个点,每一列看成一个点

    棋盘是一个很显然的二分图模型,把不能共存的行和列连边显然得到的是二分图。那么问题:一共最多取出多少行和列就转化为二分图上最大独立集问题

    二分图的最大独立集

    定义:选出一些顶点使得这些顶点两两不相邻,则这些点构成的集合称为独立集。找出一个包含顶点数最多的独立集称为最大独立集。

    方法:最大独立集=所有顶点数-最小顶点覆盖

    跑一遍匈牙利或者网络流就是了

    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #include<iostream>
    using namespace std;
    
    const int N=4e3+15;
    int n,X,tot;
    int match[N],head[N],vis[N];
    struct EDGE{
        int to,nxt;
    }edge[N];
    inline int read(){
        char ch=getchar();int s=0,f=1;
        while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
        while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
        return s*f;
    }
    void link(int u,int v){
        edge[++tot]=(EDGE){v,head[u]};
        head[u]=tot;
    }
    bool dfs(int x)
    {
        for (int i=head[x];i;i=edge[i].nxt){
            int y=edge[i].to;
            if (vis[y]) continue;
            vis[y]=1;
            if (!match[y]||dfs(match[y]))
            {
                match[y]=x;
                return 1;
            }
        }
        return 0;
    }
    int main()
    {
        freopen("phalanx.in","r",stdin);
        freopen("phalanx.out","w",stdout);
        n=read();X=read();
        for (int i=1,x,y;i<=X;i++)
        {
            x=read();y=read();
            link(x,y);
        } 
        int ans=0;
        for (int i=1;i<=n;i++)
        {
            memset(vis,0,sizeof(vis));
            if (dfs(i)) ++ans;
        } 
        printf("%d
    ",(2*n-ans)*n);
        return 0;
    }
    View Code

    T2:小凯学数学

    题目链接:

    https://jzoj.net/senior/#contest/show/2543/1

    题目:

    由于小凯上次在找零问题上的疑惑,给大家在考场上带来了很大的麻烦,他决心好好学习数学
    本次他挑选了位运算专题进行研究 他发明了一种叫做“小凯运算”的运算符:
    a$b =( (a&b) + (a|b) )>>1
    他为了练习,写了n个数在黑板上(记为a[i]) 并对任意相邻两个数进行“小凯运算”,把两数擦去,把结果留下 这样操作n-1次之后就只剩了1个数,求这个数可能是什么?
    将答案从小到大顺序输出
    0<=a[i]<=7

    题解:

    发现a数组无论如何运算结果都是在$[0,7]$这个区间里的

    于是我们定义$dp_{l,r}$表示区间$[l,r]$可以合并成的数,考虑到取值范围很小,我们可以用二进制状压起来

    发现暴力转移的时间复杂度是$O(64n^3)$的,考场上笔者认为跑不过去

    其实可以预处理从两个二进制数合并得到的新的二进制数,这样优化到了$O(n^3)$

    大水

    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    #include<iostream>
    using namespace std;
    
    const int N=150+15;
    const int M=1<<8;
    int n;
    int a[N],dp[N][N],tmp[M][M];
    int merge(int x,int y)
    {
        int re=0;
        for (int i=0;i<=7;i++)
        if (x&(1<<i))
        {
            for (int j=0;j<=7;j++)
            if (y&(1<<j))
            {
                int p=(i+j)/2;
                re|=1<<p;    
            }
        }
        return re;
    }
    void init()
    {
        for (int i=0;i<M;i++)
            for (int j=0;j<M;j++)
                tmp[i][j]=merge(i,j);
    }
    int main()
    {
        freopen("math.in","r",stdin);
        freopen("math.out","w",stdout);
        init();
        scanf("%d",&n);
        for (int i=1;i<=n;i++) scanf("%d",a+i),dp[i][i]=1<<a[i];
        for (int len=2;len<=n;len++)
        {
            for (int l=1;l<=n;l++)
            {
                int r=l+len-1;
                if (r>n) continue;
                for (int k=l;k<r;k++)
                {
                    dp[l][r]|=tmp[dp[l][k]][dp[k+1][r]];
                }
            }
        }
        for (int i=0;i<=7;i++) if (dp[1][n]&(1<<i)) printf("%d ",i);
        return 0;
    }
    View Code

    T3:逛公园 

    题目链接:

    https://jzoj.net/senior/#contest/show/2543/2

    题目:

    q次询问,每次给出l,r,要求输出答案

    题解:

    考虑$O(qn)$怎么做?(这种复杂度在考场上得到了完美的85分)。暴力从 l 循环到 r,每次的起始值都是逛完上个景点的最大值和X0的最大值这样对于每个询问都能 O(n)得出答案

    以下内容大部分来自题解

    记 $F(i,j,x_0)$表示以初始代价 $x_0$ 依次经过第 $i$ 景点到第 $j$ 景点后的答案 有两个性质:

    1.对于 $a>=b,F(i,j,a)>=F(i,j,b)$

    2.记 $G(i,j)$为 $F(i,j,inf)$,其中 $inf $是正无穷,$S(i,j)$为 $i$ 到 $j$ 的 $D$ 值之和。 则有 $F(i,j,x0)=min( G(i,j), x0+S(i,j) )$。($G(i,j)$就是一定会在$l$处被卡。这个性质没有严谨的证明,感性理解一下)

    推论:对于询问的 $l,r$,如果两个子串都被包含在$[l,r]$中,且有 $G1>=G2 且 S1>=S2$,显然第二个子串是一定不会取到的(由性质二得到)。

    那么考虑对于一段区间我们怎么做?首先我们把所有的子区间取出来,按$G$值从小到大排序,维护单调栈去掉一定不会取到的子区间,那么剩下的显然就是$G$值单调递增,$S$值单调递减一个个子区间。由于我们是取min,可以在这段区间上二分(当然三分大概也可以,但好像有点傻),找到那个$G$和$S+x0$最接近的区间就是目标区间

    发现这样的复杂度还不如之前的做法,但是我们可以分块

    块内的贡献:每块大小$sqrt{n}$,子串个数就是 $O(n)$个可以把每个子串 的 $G$ 值和 $S$ 值都求出来,根据推论把没用的都扔掉。每次询问给出 $X_0$,最大的点一定中间某处(答案是 min 函数),二分即可得出块内的答案

    块间的贡献:一共有 2 种

    1. 从当前块开始到后面的某一块结束

    2. 从之前的某一块开始到当前块结束

    参照暴力的策略,那么我们就可以维护一个 $Y$ 值代表上一个块给 这一个块带来的贡献,同时再维护前缀以及后缀的 $G、S$ 值

    利用前缀和 $Y$ 值,我们可以采用和之前块内一样的二分来算出答案

    利用后缀我们可以算出这一块给下一块的 $Y$ 值,也有三种情况:

    1 从上个 $Y$ 走满整块到下个 $Y$

    2 从某个后缀开始

    3 直接取 $X_0$

    三者的最大值即为 $Y$

    对于每次询问,我们散块暴力,整块二分再连接就好了

    时间复杂度应该是$O(nsqrt{n}logn+qsqrt{n}logn)$

    好像只是比暴力好那么一点

    我的代码跑的好慢

    #include<algorithm>
    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<vector>
    #include<cmath>
    using namespace std;
    
    const int N=5e4+15;
    const int M=250+15;
    const int inf=1e9;
    int n,q,blo,tcnt,tp;
    int d[N],li[N],belong[N],st[N],ed[N],sumd[N];
    int F[M][M][M];
    struct node{
        int G,S;
    }T[N],sta[N];
    bool operator < (node a,node b) {return a.G<b.G||(a.G==b.G&&a.S<b.S);}
    vector <node> vec[M],pre[M],suf[M];
    inline int read(){
        char ch=getchar();int s=0,f=1;
        while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
        while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
        return s*f;
    }
    int G(int l,int r) 
    {
        return F[belong[l]][l-st[belong[l]]][r-st[belong[l]]];
    }
    int S(int l,int r) 
    {
        return sumd[r]-sumd[l-1];
    }
    void calc(vector <node> &V)
    {
        //printf("%d
    ",T[tcnt].S);
        sort(T+1,T+1+tcnt);
        tp=0;
        sta[++tp]=T[1];
        for (int i=2;i<=tcnt;i++)
        {
            while (tp&&T[i].G>=sta[tp].G&&T[i].S>=sta[tp].S) tp--;
            sta[++tp]=T[i];
        }
        for (int i=1;i<=tp;i++) V.push_back(sta[i]);
    }
    void init(int id)
    {
        for (int l=st[id];l<=ed[id];l++)
        {
            int x0=inf;
            for (int r=l;r<=ed[id];r++)
            {
                x0=min(li[r],d[r]+x0);
                F[id][l-st[id]][r-st[id]]=x0;
            }
        }
        tcnt=0;
        for (int l=st[id];l<=ed[id];l++)
            for (int r=l;r<=ed[id];r++)
            {
                T[++tcnt]=(node){G(l,r),S(l,r)};
            }
        calc(vec[id]);
        tcnt=0;
        for (int r=st[id];r<=ed[id];r++)
        {
            T[++tcnt]=(node){G(st[id],r),S(st[id],r)};
        }    
        calc(pre[id]);
        tcnt=0;
        for (int l=st[id];l<=ed[id];l++)
        {
            T[++tcnt]=(node){G(l,ed[id]),S(l,ed[id])};
        }
        calc(suf[id]);
    }
    int ef(vector <node> Vec,int x0)
    {
        int l=0,r=Vec.size()-1;
        while (l+1<r)
        {
            int mid=l+r>>1;
            if (Vec[mid].G>Vec[mid].S+x0) r=mid;
            else l=mid;
        }
        int re=min(Vec[l].G,Vec[l].S+x0);
        re=max(re,min(Vec[r].G,Vec[r].S+x0));
        if (l+1<Vec.size()-1)
        {
            ++l;
            re=max(re,min(Vec[l].G,Vec[l].S+x0));
        }
        return re;
    }
    void solve(int l,int r,int x0)
    {
        if (belong[l]==belong[r])
        {
            int mx=x0,Y=x0;
            for (int i=l;i<=r;i++)
            {
                Y=min(max(Y,x0)+d[i],li[i]);
                mx=max(mx,Y);
            }
            printf("%d
    ",mx);
            return;
        }
        int Y=x0,mx=x0;
        for (int i=l;i<=ed[belong[l]];i++)
        {
            Y=min(max(Y,x0)+d[i],li[i]);
            mx=max(mx,Y);
        }
        Y=max(Y,x0);
        for (int i=belong[l]+1;i<=belong[r]-1;i++)
        {
            mx=max(mx,ef(pre[i],Y));
            mx=max(mx,ef(vec[i],x0));
            Y=min(G(st[i],ed[i]),Y+S(st[i],ed[i]));//从上个Y走满整块到下个Y
            Y=max(Y,ef(suf[i],x0));//从某个后缀开始
            Y=max(Y,x0);//直接取X0
        }
        for (int i=st[belong[r]];i<=r;i++)
        {
            Y=min(max(Y,x0)+d[i],li[i]);
            mx=max(mx,Y);
        }
        printf("%d
    ",mx);
    }
    int main()
    {
        freopen("park.in","r",stdin);
        freopen("park.out","w",stdout);
        n=read();q=read();
        for (int i=1;i<=n;i++) d[i]=read(),sumd[i]=sumd[i-1]+d[i];
        for (int i=1;i<=n;i++) li[i]=read();
        blo=sqrt(n);
        for (int i=1;i<=n;i++) belong[i]=(i-1)/blo+1;
        for (int i=1;i<=belong[n];i++)
        {
            st[i]=(i-1)*blo+1;
            ed[i]=min(i*blo,n);
            init(i);
        }
        while (q--)
        {
            int l=read(),r=read(),x0=read();
            solve(l,r,x0);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    oracle之check约束小结
    非归档模式下使用Rman进行备份和恢复
    R中,定义一个长度为0的向量
    R中,去掉dataframe中的NA行
    Oracle数据库的后备和恢复————关于检查点的一些知识
    关于oracle修复控制文件与数据文件不一致的问题----
    《SLAM机器人基础教程》第三章 单片机与STM32:GPIO实验及Keil软件使用WatchWindows进行Debug调试
    《SLAM导航机器人基础》第三章:单片机与STM32:单片机概述和Keil开发环境配置
    《SLAM导航机器人基础》第二章:C/C++编程(后)
    《SLAM导航机器人基础》第二章:C/C++编程(中)
  • 原文地址:https://www.cnblogs.com/xxzh/p/9872890.html
Copyright © 2011-2022 走看看