zoukankan      html  css  js  c++  java
  • 7月15日考试 题解(链表+状压DP+思维题)

    前言:蒟蒻太弱了,全打的暴力QAQ。

    ---------------------

    T1 小Z的求和

    题目大意:求$sumlimits_{i=1}^n sumlimits_{j=i}^n kthmax(a_i,a_{i+1},cdots ,a_j)+kthmin(a_i,a_{i+1},cdots ,a_j)$。其中$kthmax$指第$k$大,$kthmin$指第$k$小。

    听hs-black说是链表维护,时间复杂度是$O(nk)$。然而并不会做……听了听学长的讲解。

    对于这类问题,我们肯定是考虑每个元素对于答案的贡献的。(不然绝对会T飞

    考虑$a[i]$从大到小添加,那么当有新加入的元素时,数轴为这样(假设$k=4$):

    红色为已经加入的元素,蓝色为新加入的元素,紫色是合法区间。

     

    然后这个长度为$k$的区间从左到右移动,每次对于答案的贡献为$a[x]*(l-pre[l])*(nxt[r]-r)$。当所有贡献统计完后删除这个结点。

    考虑到从大到小添加不易维护前驱和后缀,我们采用从小到大删除的方法,用链表维护。对于$kthmax$和$kthmin$只需相同方法求两遍就行了。时间复杂度$O(nk)$。

    代码:

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    const int mod=998244353;
    int n,k,a[1000005],pos[1000005],ans;
    int nxt[1000005],pre[1000005];
    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;
    }
    bool cmp(int x,int y){return a[x]<a[y];}
    inline void work()
    {
        for (int i=1;i<=n;i++) nxt[i]=i+1,pre[i]=i-1;
        for (int i=1;i<=n;i++)
        {
            int l=pos[i],r=pos[i],cnt=1;
            for (;cnt<k;cnt++)
            {
                if (pre[l]) l=pre[l];
                else break;
            }
            for (;cnt<k;cnt++)
            {
                if (nxt[r]<=n) r=nxt[r];
                else break;
            }
            if (cnt==k)
            {
                while(l<=pos[i])
                {
                    if (r==n+1) break;
                    ans+=a[pos[i]]*(nxt[r]-r)*(l-pre[l])%mod;
                    ans%=mod;
                    l=nxt[l],r=nxt[r];
                }
            }
            pre[nxt[pos[i]]]=pre[pos[i]];
            nxt[pre[pos[i]]]=nxt[pos[i]];
        }
    }
    signed main()
    {
        n=read(),k=read();
        for (int i=1;i<=n;i++) a[i]=read(),pos[i]=i;
        sort(pos+1,pos+n+1,cmp);
        work();
        reverse(pos+1,pos+n+1);
        work();
        printf("%lld",ans%mod);
        return 0;
    }

    T2 关押罪犯

    题目大意:给定一张$n$个点,$m$条边的无向图。现在要求将这些点分成几组,每组边数不能超过$k$,且最小化分组数量。求分组最小值。

    状压DP。然而我爆搜也能水不少分,$nleq 16$。挂个$dfs$的代码吧。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    int n,m,k,a[20][20],ans=20;
    inline void dfs(int now,int cnt,int sum,int start)
    {
        if (now==n){
            int tot=0;
            for (int i=start;i<n;i++) if (a[i][now]) tot++;
            if (cnt+tot<=k) ans=min(ans,sum);
            else ans=min(ans,sum+1);
            return;
        }
        int tot=0;
        for (int i=start;i<now;i++) if (a[i][now]) tot++;
        dfs(now+1,0,sum+1,now+1);
        if (cnt+tot<=k) dfs(now+1,cnt+tot,sum,start);
    }
    int main()
    {
        cin>>n>>m>>k;
        for (int i=1;i<=m;i++)
        {
            int x,y;cin>>x>>y;
            a[x][y]=a[y][x]=1;
        }
        dfs(1,0,1,1);
        cout<<ans;
        return 0;
    }

    T3 CF348D Turtles

    引理:LGV定理。完全不会。听wyx大佬说可以不用定理,因为只有两条路径直接做就可以。

    其实是一道思维题。

    题意可以简化为从$(1,2)$到$(n-1,m)$和从$(2,1)$到$(n,m-1)$的路径不相交的方案数。然后,本题的精髓来了:

    如果两条路径有相交,那么可以理解为这种情况是从$(1,2)$到$(n,m-1)$和从$(2,1)$到$(n-1,m)$的路径。这种情况是不合法的。所以我们只需要一步容斥一下,那么答案就是:

    $calc(1,2,n-1,m)*calc(2,1,n,m-1)-calc(1,2,n,m-1)*clac(2,1,n-1,m)$

    代码难度普及-,思维难度提高+。

    代码:

    //calc(1,2,n-1,m)*calc(2,1,n,m-1)-calc(2,1,n-1,m)*calc(1,2,n,m-1)
    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    const int mod=1e9+7;
    long long n,m,f[3005][3005];
    char vis[3005][3005];
    inline int calc(int a,int b,int c,int d)
    {
        memset(f,0,sizeof(f));
        for (int i=a;i<=c;i++)
            for (int j=b;j<=d;j++)
            {
                if (vis[i][j]=='.'){
                    if (i==a&&j==b) f[i][j]=1;
                    else f[i][j]=(f[i-1][j]+f[i][j-1])%mod;
                }
            }
        return f[c][d];
    }
    signed main()
    {
        cin>>n>>m;
        for (int i=1;i<=n;i++) scanf("%s",vis[i]+1);
        int t1=calc(1,2,n-1,m),t2=calc(2,1,n,m-1);
        int t3=calc(1,2,n,m-1),t4=calc(2,1,n-1,m);
        cout<<((t1*t2%mod-t3*t4%mod)+mod)%mod;
        return 0;
    } 
  • 相关阅读:
    算法之冒泡排序
    实现秒杀的几个想法(续)
    乐观锁
    wifi-sdio接口
    解压vmlinuz和解压initrd(initramfs)
    supplicant
    wpa_supplicant测试
    qu
    netlink
    wpa_supplicant安装
  • 原文地址:https://www.cnblogs.com/Invictus-Ocean/p/13308356.html
Copyright © 2011-2022 走看看