• 8月21日考试 题解(前缀和+差分+贪心+二分答案+缩点+建图优化)


    今天考的还行,主要暴力分给力233

    T1 棋盘

    题目大意:给定一张$n*n$的棋盘,每个格子上是黑色或白色。现在有一次机会将一个$k*k$的区域染成白色。问操作过后全部为白色的行+全部为白色的列最多有多少。

    正解是前缀和+差分。然而因为时限比较宽松,打了一个$(n-k+1)^2k$的暴力也能过2333。姑且看看吧。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    int a[2005][2005],n,k,sum1[2005][2005],sum2[2005][2005];
    int sum,ans;
    char ch[2005][2005];
    inline void solve(int x,int y)
    {
        int res=0;
        for (int i=x;i<=x+k-1;i++)
        {
            int s1=sum1[i][y+k-1]-sum1[i][y-1];
            if (sum1[i][n]&&s1==sum1[i][n]) res++;
        }
        for (int i=y;i<=y+k-1;i++)
        {
            int s2=sum2[x+k-1][i]-sum2[x-1][i];
            if (sum2[n][i]&&s2==sum2[n][i]) res++;
        }
        ans=max(ans,sum+res);
    }
    int main()
    {
        scanf("%d%d",&n,&k);
        for (int i=1;i<=n;i++)
        {
            scanf("%s",ch[i]+1);
            for (int j=1;j<=n;j++)
            {
                a[i][j]=(ch[i][j]=='B');
                sum1[i][j]=sum1[i][j-1]+a[i][j];
                sum2[i][j]=sum2[i-1][j]+a[i][j];
            }
        }
        for (int i=1;i<=n;i++)
        {
            if (!sum1[i][n]) sum++;
            if (!sum2[n][i]) sum++;
        }
        for (int i=1;i<=n-k+1;i++)
            for (int j=1;j<=n-k+1;j++) solve(i,j);
        printf("%d",ans);
        return 0;
    }

    T2 序列

    题目大意:给定一个长度为$n$的序列。现将其划分成若干个区间,使得区间内最大值减最小值的和最大。求出这个最大值。

    一眼看出$n^2$的DP。设$f[i]$表示考虑到$i$时的最大值,显然有$f[i]=maxlimits_{0leq jleq i-1}(f[i],f[j]+qmax-qmin)$,$qmax$和$qmin$指$[j+1,i]$内的最值。

    然而正解是$O(n)$的贪心+递推。序列内元素的值变化可以看成一条波浪,而划分一定在波峰和波谷,不然不是最优的。我们所要考虑的是在最值的左边还是右边划一刀。这样我们可以从左到右扫一遍序列进行决策即可。

    代码:

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    const int inf=1e18;
    const int maxn=10000005;
    const int maxm=500005;
    const int mod=1<<30;
    int a[maxn],b[maxn];
    int x,y,z,m,p[maxm],l[maxm],r[maxm],n;
    int maxx=0,minn=inf,ans[2],pre;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    signed main()
    {
        n=read();
        x=read(),y=read(),z=read(),b[1]=read(),b[2]=read(),m=read();
        for (int i=3;i<=n;i++) b[i]=(x*b[i-1]+y*b[i-2]+z)%mod;
        for (int i=1;i<=m;i++)
        {
            p[i]=read(),l[i]=read(),r[i]=read();
            for (int j=p[i-1]+1;j<=p[i];j++)
                a[j]=b[j]%(r[i]-l[i]+1)+l[i];
        }
        ans[0]=-inf,ans[1]=0;
        for (int i=1;i<=n;i++)
            if (i>1&&i<n&&((a[i-1]<a[i]&&a[i]>=a[i+1])||(a[i-1]>a[i]&&a[i]<=a[i+1])))
            {
                int tmp[2]={-inf,-inf};
                tmp[0]=max(tmp[0],ans[0]+max(maxx,a[pre])-min(minn,a[pre]));
                if (pre!=i-1) tmp[0]=max(tmp[0],ans[1]+maxx-minn);
                else tmp[0]=max(tmp[0],ans[1]);
                tmp[1]=max(tmp[1],ans[0]+max(a[pre],max(maxx,a[i]))-min(a[pre],min(minn,a[i])));
                tmp[1]=max(tmp[1],ans[1]+max(maxx,a[i])-min(minn,a[i]));
                ans[0]=tmp[0],ans[1]=tmp[1];
                maxx=0,minn=inf,pre=i;
            }
            else maxx=max(maxx,a[i]),minn=min(minn,a[i]);
        printf("%lld",max(ans[0]+max(maxx,a[pre])-min(minn,a[pre]),ans[1]+maxx-minn));
        return 0;
    }

    T3 游戏

    给定一张$n$个点$m$条边的无向图,$q$次询问,支持点向区间连边。显然一共有$q+1$张图。定义合法点对$(u,v)$为从$u$出发经过$x$到$v$的路径个数是有限的,路径可以重复经过点和边但不能相同。如果一张图的合法点对个数不少于$k$,我们就说这张图是合法的。现在问你这$q+1$张图中有多少个是合法的。

    二分答案+缩点+优化建图。

    首先合法的图不可能超过$k+1$个,所以我们不妨进行二分答案。

    不难想到如果$u->x->v$的路径上有点是在环上的话那么$(u,v)$肯定不是合法点对。所以我们不妨进行缩点,然后找出满足条件的点,然后乘法原理进行统计。

    对于支持点向区间连边,我们可以$nlog n$建出每个区间对应的虚点,虚点向区间内所有点连边。当需要向区间连边的时候直接向虚点连边即可。

    其实点向区间连边也可以用线段树优化建图,但我并不会写QAQ。之前写过但考试肯定写不出来……

    代码:

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<stack>
    #include<vector>
    using namespace std;
    const int maxn=200005;
    int n,m,x0,q,num,maxlen,last;long long k;
    int x[maxn],l[maxn],r[maxn];
    int siz[maxn],pos[maxn],dfn[maxn],low[maxn],vis[maxn],jishu,tot;
    int first[maxn],t1[maxn],t2[maxn],single[maxn];
    int head[maxn],cnt,backup[maxn],backupsz;
    struct node
    {
        int next,to;
    }edge[maxn*25];
    vector<int> v1[maxn],v2[maxn];
    stack<int> st;
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
        while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    inline void add(int from,int to)
    {
        edge[++cnt].next=head[from];
        edge[cnt].to=to;
        head[from]=cnt;
    }
    inline void tarjan(int now)
    {
        dfn[now]=low[now]=++jishu;
        vis[now]=1;st.push(now);
        for (int i=head[now];i;i=edge[i].next)
        {
            int to=edge[i].to;
            if (!dfn[to]) tarjan(to),low[now]=min(low[now],low[to]);
            else if (vis[to]) low[now]=min(low[now],dfn[to]);
        }
        if (low[now]==dfn[now])
        {
            tot++;
            while(st.top()!=now)
            {
                int x=st.top();st.pop();
                vis[x]=0;
                pos[x]=tot;
            }
            int x=st.top();st.pop();
            vis[x]=0;
            pos[x]=tot;
        }
    }
    inline void link(int x0,int l0,int r0,int len)
    {
        if (l0>r0) return;
        if (r0-l0+1<len){link(x0,l0,r0,len>>1);return;}
        int l1=(l0-1)/len+1;
        if (len*(l1-1)+1==l0) add(x0,first[len]+l1),link(x0,len*l1+1,r0,len);
        else link(x0,l0,len*l1,len),link(x0,len*l1+1,r0,len);
        return;
    }
    inline long long check(int mid)
    {
        memcpy(backup,head,sizeof(backup));
        backupsz=cnt;
        for (int i=last+1;i<=mid;i++) link(x[i],l[i],r[i],maxlen);
        memset(siz,0,sizeof(siz));
        memset(pos,0,sizeof(pos));
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        jishu=tot=0;
        for (int i=1;i<=num;i++) if (!dfn[i]) tarjan(i);
        for (int i=1;i<=tot;i++) single[i]=0;
        for (int i=1;i<=num;i++) v1[i].clear(),v2[i].clear();
        for (int i=1;i<=n;i++) single[pos[i]]=1,siz[pos[i]]++;
        for (int i=1;i<=num;i++)
            for (int j=head[i];j;j=edge[j].next)
            {
                int u=pos[i],v=pos[edge[j].to];
                if (u!=v) v1[u].push_back(v),v2[v].push_back(u);
                if (i<=n&&edge[j].to<=n&&u==v) single[u]=0;
            }
        memset(t1,0,sizeof(t1));
        memset(t2,0,sizeof(t2));
        t1[pos[x0]]=t2[pos[x0]]=1;
        int cnt1=0,cnt2=0,c1=n,c2=n;
        for (int i=1;i<=tot;i++)
        {
            if (!t2[i]) continue;
            c2-=siz[i];
            for (int j=0;j<v2[i].size();j++) t2[v2[i][j]]=1;
        }
        for (int i=1;i<=tot;i++)
            if (single[i]||!siz[i]) t2[i]=0;
        for (int i=1;i<=tot;i++)
        {
            if (!t2[i]) continue;
            cnt2+=siz[i];
            for (int j=0;j<v2[i].size();j++) t2[v2[i][j]]=1;
        }
        for (int i=tot;i>=1;i--)
        {
            if (!t1[i]) continue;
            c1-=siz[i];
            for (int j=0;j<v1[i].size();j++) t1[v1[i][j]]=1;
        }
        for (int i=tot;i>=1;i--)
            if (single[i]||!siz[i]) t1[i]=0;
        for (int i=tot;i>=1;i--)
        {
            if (!t1[i]) continue;
            cnt1+=siz[i];
            for (int j=0;j<v1[i].size();j++) t1[v1[i][j]]=1;    
        } 
        return (long long)(n-cnt1)*(long long)(n-cnt2)+(long long)c1*(long long)cnt2+(long long)c2*(long long)cnt1;
    }
    inline void reset()
    {
        memcpy(head,backup,sizeof(head));
        cnt=backupsz;
    }
    signed main()
    {
        n=read();m=read();x0=read();q=read();k=read();
        for (int i=1;i<=m;i++)
        {
            int u=read(),v=read();
            add(u,v);
        }
        num=n,maxlen=1;
        for (int i=2;i<=n;i<<=1)
        {
            first[i]=num,maxlen=i;
            for (int j=1;j+i-1<=n;j+=i)
            {
                num++;
                for (int k=j;k<j+i;k++) add(num,k);
            }
        }
        for (int i=1;i<=q;i++) x[i]=read(),l[i]=read(),r[i]=read();
        int l0=0,r0=q+1;
        while(l0<r0)
        {
            int mid=(l0+r0+1)/2ll;
            if (check(mid-1)>=k) l0=mid,last=mid-1;
            else r0=mid-1,reset();
        }
        printf("%d",l0);
        return 0;
    }
  • 相关阅读:
    小游戏——js+h5[canvas]+cs3制作【五子棋】小游戏
    CSS-自定义高度的元素背景图如何自适应以及after伪元素在ie下的处理
    JS-制作留言提交系统(支持ctrl+回车)
    JS-键盘事件之方向键移动元素
    JS-鼠标跟随块(一个小圆点跟着鼠标跑)
    JS-鼠标彩色拖尾小效果
    JS-获取class类名为某个的元素-【getClass】函数封装
    JS-倒计时效果
    无法打开内核设备“\.Globalvmx86”: 系统找不到指定的文件。
    Zookeeper的简单理解
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/13542992.html
Copyright © 2020-2023  润新知