• 2021牛客暑期多校训练营8


    比赛链接:https://ac.nowcoder.com/acm/contest/11259

    A,D,E,J,K,10,不错。开场E题签到,一小时左右做出构造题K。开始看D和A;A读了半天题,写了写,WA了;又改了改,两小时的时候过了。D过了一会也做出来了。讨论了一番J,发现是要递归,四小时多一点过了。然后F题有个感觉很对的做法,最后三十五分钟开始写,写完WA了,也不知哪里错了?似乎也没有别人用这个做法了。

    交流有时候挺费劲,双方都有原因。我这方面的课题,是应对我所在的izhi。主要是关于kimochi和zhizilioku啦。适应或改变?kimochi或zhizilioku?

    当然了,我全都要!

    D

    分析:

    一开始摸不着头脑,只想到由于(c)序列,所以确定(a_1)就确定了整个(a)序列。然后利用(b)的部分信息,可以确定每个(a_i)的一个大致范围……

    就是这题,三人一起想;交流得很不顺畅。后来G说想出来了,口胡了一堆什么,然后写了一会,过了。行吧。

    现在补题,原来是有一个“关键观察”:( a_i + a_{i+1} = ( a_i | a_{i+1} ) + ( a_i & a_{i+1} ) ),妙啊!然后就可以分开每一位考虑了,而且确定( a_1 )整个(a)就确定了。

    原来如此简单,呵。

    代码如下:

    #include<iostream>
    #define ll long long
    using namespace std;
    int const N=1e5+5;
    int n,b[N],c[N];
    int main()
    {
        scanf("%d",&n);
        for(int i=2;i<=n;i++)scanf("%d",&b[i]);
        for(int i=2;i<=n;i++)scanf("%d",&c[i]),c[i]-=b[i];
        ll ans=1;
        for(int t=0;t<30;t++)
        {
            int f0=1,f1=1,n0=0,n1=1;
            for(int i=2;i<=n;i++)
            {
                if((b[i]&(1<<t))&&(c[i]&(1<<t)))
                {
                    if(f0){if(!n0)f0=0;  else n0=1;}
                    if(f1){if(!n1)f1=0;  else n1=1;}
                }
                if((b[i]&(1<<t))&&!(c[i]&(1<<t)))
                {
                    if(f0)n0^=1;
                    if(f1)n1^=1;
                }
                if(!(b[i]&(1<<t))&&(c[i]&(1<<t)))
                {
                    puts("0");
                    return 0;
                }
                if(!(b[i]&(1<<t))&&!(c[i]&(1<<t)))
                {
                    if(f0){if(n0)f0=0;  else n0=0;}
                    if(f1){if(n1)f1=0;  else n1=0;}
                }
                if(!f0&&!f1)break;
            }
            //printf("t=%d f0=%d f1=%d
    ",t,f0,f1);
            ans*=(f0+f1);
        }
        printf("%lld
    ",ans);
        return 0;
    }
    me

    E

    分析:

    签到题。判断一个年份是否既是闰年又是质数。其实直接输出“no”就行,是闰年怎么可能是质数呢?哈哈。

    F

    分析:

    比赛时想了各种连边、图之类的做法,但都没成功。这题其实可以暴力一点,因为没有特别精细的要求,走的方式也很有规律。

    第一种和第二种机器人都很容易做,我们看第三种:

    考虑对行进行分治。对于当前行的范围( l,r ),关注( mid=frac{l+r}{2} )这一行;用它来解决起点在( mid )上方、终点在( mid )下方的询问。

    怎么解决呢?由于起点到终点的路线一定会经过这一行的某个点,我们可以枚举这一点,然后bfs求出这一点向上能走到的区域和向下能走到的区域;如果同时包含起点和终点,那么问题就解决了。这样暴力做,时间复杂度是( O(n^3) )的。

    然后,起点终点都在( mid )上方的询问递归到上半部分解决,都在( mid )下方的询问递归到下半部分解决。

    这样,解决询问的时间复杂度是( O(qlogn) ),因为每个询问均摊( O(logn) );分治的每一层时间复杂度( O(n^3) ),而分治一共有( logn )层。总复杂度是( O(qlogn+n^3logn) )。有点大。

    但是,还有优化的余地。找( mid )这一行中每个点能走到的区域时,有很多重复的路线,因为这些点彼此可走到的区域有很多重复。如何加快这个过程呢?

    实际上,我们也可以用DP来做这个过程;如果全图每个点( (i,j) )记录一个( dp[i][j] )表示能走到( mid )中的哪些点,转移是很容易的。然而,如果我们直接让两个点之间状态( O(n) )地转移,复杂度就是( O(n^3) )。和( mid )行上每个点来一遍bfs异曲同工。

    但是,我们发现( dp[i][j] )记录的东西是能否走到( mid )行的每个点。换言之,是一系列的( 0 )和( 1 )。而转移就是把能走到当前格子的那些格子状态中的( 1 )直接拿过来。

    所以我们可以用bitset优化,用bitset来存状态,转移就是( | )操作。这下,转移的时间复杂度就变成( O(frac{n}{32}) )了!

    这样就能过了。时间复杂度( O(qlogn + frac{n^3logn}{32}) )。

    写的时候注意分治边界和询问的特判。

    代码如下:

    #include<iostream>
    #include<vector>
    #include<bitset>
    #define mid ((l+r)>>1)
    #define pb push_back
    using namespace std;
    int const N=505,M=5e5+5;
    int n,m,q,ans[M];
    char s[N][N];
    bitset<N>f[N][N],g[N][N];
    struct Nd{
        int x1,y1,x2,y2,id;
    };
    int work(int t,int x1,int y1,int x2,int y2)
    {
        if(t==1)
        {
            if(y1!=y2)return 0; if(x1>x2)return 0;///
            for(int i=x1;i<=x2;i++)
                if(s[i][y1]=='1')return 0;
            return 1;
        }
        if(t==2)
        {
            if(x1!=x2)return 0; if(y1>y2)return 0;///
            for(int j=y1;j<=y2;j++)
                if(s[x1][j]=='1')return 0;
            return 1;
        }
    }
    void solve(int l,int r,vector<Nd> v)
    {
        if(l>r)return;///
        for(int j=m;j;j--)
        {
            f[mid][j].reset();
            if(s[mid][j]=='1')continue;
            f[mid][j][j]=1; f[mid][j]|=f[mid][j+1];
        }
        for(int j=1;j<=m;j++)
        {
            g[mid][j].reset();
            if(s[mid][j]=='1')continue;
            g[mid][j][j]=1; g[mid][j]|=g[mid][j-1];
        }
        for(int i=mid-1;i>=l;i--)
            for(int j=m;j;j--)
            {
                f[i][j].reset();
                if(s[i][j]=='1')continue;
                f[i][j]|=f[i][j+1]; f[i][j]|=f[i+1][j];
            }
        for(int i=mid+1;i<=r;i++)
            for(int j=1;j<=m;j++)
            {
                g[i][j].reset();
                if(s[i][j]=='1')continue;
                g[i][j]|=g[i][j-1]; g[i][j]|=g[i-1][j];
            }
        vector<Nd>vl,vr;
        for(Nd it:v)
        {
            if(it.x2<mid)vl.pb(it);
            else if(it.x1>mid)vr.pb(it);
            else ans[it.id]=(f[it.x1][it.y1]&g[it.x2][it.y2]).any();
                //printf("mid=%d id=%d ans=%d
    ",mid,it.id,ans[it.id]);
        }
        v.clear();
        if(vl.size())solve(l,mid-1,vl);
        if(vr.size())solve(mid+1,r,vr);
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)scanf("%s",s[i]+1);
        scanf("%d",&q); vector<Nd>v;
        for(int i=1,t,x1,y1,x2,y2;i<=q;i++)
        {
            scanf("%d%d%d%d%d",&t,&x1,&y1,&x2,&y2);
            if(t==1||t==2)ans[i]=work(t,x1,y1,x2,y2);
            else
            {
                if(x1<x2&&y1>y2)continue;
                v.pb((Nd){x1,y1,x2,y2,i});
            }
        }
        solve(1,n,v);
        for(int i=1;i<=q;i++)puts((ans[i])?"yes":"no");
        return 0;
    }
    me

    J

    分析:

    把问题简化一下。找出( s )到( t )的那条链,链上每个点延伸出去的部分只有最长链是需要考虑的。所以现在问题是两人分别从( s )和( t )出发,相互靠近,某一时刻可以从链上离开走那个点的最长链,剩下那个人就可以在剩下的节点中任意选择一个进入其最长链,这样一个博弈问题。

    可以直接用递归模拟这个博弈过程,用ST表快速找到剩下那个人应该走哪个点的最长链。

    递归时注意状态的含义!想当然地写最优决策而忽略当前状态的话,会在某一两个点一直WA。

    代码如下:

    #include<iostream>
    #include<cstring>
    #include<cmath>
    using namespace std;
    int const N=5e5+5,inf=1e9;
    int n,s,t,hd[N],cnt,nxt[N<<1],to[N<<1],fa[N],dep[N],p[N],pc;
    int st1[N][21],st2[N][21];
    bool is[N];
    void add(int x,int y){nxt[++cnt]=hd[x]; hd[x]=cnt; to[cnt]=y;}
    void dfs(int u)
    {
        dep[u]=0;
        for(int i=hd[u],v;i;i=nxt[i])
        {
            if((v=to[i])==fa[u]||is[v])continue;
            fa[v]=u; dfs(v);
            dep[u]=max(dep[u],dep[v]+1);
        }
    }
    /*
    int get1(int l,int r)
    {
        int ret=0;
        for(int i=20;i>=0;i--)
            if(l+(1<<i)-1<=r)ret=max(ret,st1[l][i]),l+=(1<<i);
        return ret;
    }
    int get2(int l,int r)
    {
        int ret=0;
        for(int i=20;i>=0;i--)
            if(l+(1<<i)-1<=r)ret=max(ret,st2[l][i]),l+=(1<<i);
        return ret;
    }
    */
    int get1(int l,int r)
    {
        int j=(int)log2(r-l+1);
        return max(st1[l][j],st1[r-(1<<j)+1][j]);
    }
    int get2(int l,int r)
    {
        int j=(int)log2(r-l+1);
        return max(st2[l][j],st2[r-(1<<j)+1][j]);
    }
    int work2(int u,int v);
    int work1(int u,int v)//一人在u,一人在v时
    {
        if(u==v-1)return st1[u][0]-st2[v][0];//st1[u][0]-get2(v,pc);
        return max(st1[u][0]-get2(u+1,v),work2(u+1,v));///not "get2(u+1,pc)"
    }
    int work2(int u,int v)
    {
        if(u==v-1)return st1[u][0]-st2[v][0];//get1(1,u)-st2[v][0];
        return min(get1(u,v-1)-st2[v][0],work1(u,v-1));///not "get1(1,v-1)"
    }
    int main()
    {
        scanf("%d%d%d",&n,&s,&t);
        for(int i=1,u,v;i<n;i++)
            scanf("%d%d",&u,&v),add(u,v),add(v,u);
        dfs(t); int nw=s; p[++pc]=s; is[s]=1;
        while(nw!=t)nw=fa[nw],p[++pc]=nw,is[nw]=1;
        for(int i=1;i<=pc;i++)dfs(p[i]);
        for(int i=1;i<=pc;i++)
            st1[i][0]=i-1+dep[p[i]],st2[i][0]=pc-i+dep[p[i]];
        for(int j=1;j<=20;j++)
            for(int i=1;i+(1<<j)-1<=pc;i++)
                st1[i][j]=max(st1[i][j-1],st1[i+(1<<(j-1))][j-1]),
                st2[i][j]=max(st2[i][j-1],st2[i+(1<<(j-1))][j-1]);
        printf("%d
    ",work1(1,pc));
        return 0;
    }
    me
  • 相关阅读:
    第四周学习进度总结
    SOA面向服务的架构
    MVC架构模式
    大型网站技术架构阅读笔记01
    Python爬虫出错
    修改安卓的gradle地址后出现cannot resolve symbol ......错误
    一线架构师阅读笔记03
    周进度报告(十)
    周进度报告(九)
    一线架构师阅读笔记02
  • 原文地址:https://www.cnblogs.com/Zinn/p/15122479.html
Copyright © 2020-2023  润新知