zoukankan      html  css  js  c++  java
  • HGOI 20190711 题解

    Problem A 矩阵第K小数

    给定一个$n imes m$的矩阵,位置$A_{i,j}  = i imes j$,

    给出$Q$个询问,每一次查询矩阵中第$Q_i$小的数是多少。

    对于100%的数据 , $1 leq n,m leq 10^9 , Q leq 100 , 1 leq Q_i leq n imes m$

    Sol : 本题采用暴力模拟的复杂度是不能通过的,并且其矩阵的排布都是有规律的。

       第$i$行构成的数列是公差为$i$的等差数列。 可以考虑枚举每一行,然后就可以O(1) 计算这一行有多少比某一数$x$小了。

      同时,对于每一个询问二分答案,然后可以扫一遍$n$然后二分计算每一行的贡献。这样的复杂度就是$O(Qnlog_{2} (nm) )$

      同时考虑,每一行贡献是类似于$lfloor frac{x}{i} floor$的东西,我们会发现这个东西对于$i$递增,它可能的取值只会有$sqrt{x}$种。

      并且其值是单调下降的。 所以我们可以$O(sqrt{n})$每次遍历每一个"平台"。但是由于需要二分答案,这样的复杂度会变成$log_2 n sqrt{n}$

      总时间复杂度会变成$O(Qsqrt{n} log_{2} (nm) log_2 n)$无法通过。

      如何在$O(sqrt{n})$的复杂度遍历每一个块,然后统一计算是本题的瓶颈。

      问题等价于求$sumlimits_{i=1}^n lfloor frac{x}{i} floor $,我们可以考虑一个$i$满足$a = lfloor frac{x}{i} floor $ 

      在某一个区间内,有$a leq lfloor frac{x}{i} floor < a+1 $ ,所以可以解得此时的$i in  [left lfloor frac{x}{left lfloor frac{x}{i}  ight floor+1} ight floor + 1, left lfloor frac{x}{left lfloor frac{x}{i} ight floor} ight floor]$

      所以最后的复杂度就可以做到$O(Qlog_2 (nm) sqrt{n} )$ 了。

    # pragma GCC optimize(3)
    # include <bits/stdc++.h>
    # define int long long 
    using namespace std;
    int n,m,q;
    inline int read()
    {
        int X=0,w=0; char c=0;
        while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
        while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
        return w?-X:X;
    }
    int calc(int i,int x){
        if (x%i==0) return min(x/i-1,m);
        else return min(x/i,m);
    }
    bool check(int x,int k)
    {
        int ret=0;
        for (int l=1,r;l<=min(x,m);l=r+1) {
            r=x/(x/l);
            ret+=min((x/l),n)*(r-l+1);
        } 
        return (ret>=k);
    }
    signed main()
    {
        n=read();m=read();q=read();
        while (q--) {
            int x=read(); 
            int l=1,r=n,ans;
            while (l<=r) {
                int mid=(l+r)>>1;
                if (check(mid,x)) ans=mid,r=mid-1;
                else l=mid+1;
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    mat.cpp

    Problem B 最小极差生成树

    给出$n$个点$m$条边的连通图$G=(V,E)$,求出一棵生成树使得其最大边权和最小边权的差最小。

    对于100%的数据$1 leq mleq 4000 ,  1 leq n leq 1000$

    Sol : 枚举每一条边,以这条边权$s$为下界跑最小生成树,找出最小生成树的最大边权为$d$.

      用$d-s$更新答案。注意可能会有不合法情况,输出-1即可。

    # pragma GCC optimize(3)
    # include <bits/stdc++.h>
    using namespace std;
    const int N=4e3+10;
    int n,m,f[N];
    struct node{ int u,v,w;}E[N];
    bool cmp(node a,node b) {return a.w<b.w;}
    inline int read()
    {
        int X=0,w=0; char c=0;
        while(c<'0'||c>'9') {w|=c=='-';c=getchar();}
        while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
        return w?-X:X;
    }
    int father(int x) {
        if (f[x]==x) return x;
        return f[x]=father(f[x]);
    }
    int MST(int W)
    {
        for (int i=1;i<=n;i++) f[i]=i;
        int tmp=0,o=0;
        for (int i=1;i<=m;i++) {
            if (E[i].w<W) continue;
            int fx=father(E[i].u),fy=father(E[i].v);
            if (fx==fy) continue;
            tmp++; f[fx]=fy; o=max(o,E[i].w);
            if (tmp==n-1) break;
        }
        if (tmp!=n-1) return -1;
        else return o;
    }
    int main()
    {
        int T=read();
        while (T--) {
            n=read();m=read();
            for (int i=1;i<=m;i++) {
                int u=read(),v=read(),w=read(); 
                E[i]=(node){u,v,w};
            }
            sort(E+1,E+1+m,cmp);
            int ans=-1;
            for (int i=1;i<=m;i++) {
                int t=MST(E[i].w);
                if (t==-1) continue;
                if (ans==-1) ans=t-E[i].w;
                else ans=min(ans,t-E[i].w);
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
    min.cpp

    Problem C 数字分组

    个体互异而值可能相同$n$个数$a_i$划分为若干组。 对于某一组记下这组元素的极差(最大数-最小数)

    一种合法的划分要求每一组元素极差的和有$leq M$的限制。求出所有合法划分的总数$mod 10^9 + 7$的值。

    对于20%的数据 ,$1 leq n leq 10$ ; 对于另外20%的数据,$M=0$

    对于100%的数据,$1 leq n leq 200,0 leq M leq 1000,1 leq a_i leq 500 $

    Sol : 对于20%数据直接第二类斯特林数求和然后乘起来即可。

      对于100%的数据考虑DP。

      先对数据排序,记$f_{i,j,k}$表示前$i$个数,还有$j$个划分集可以添加元素,当前差值和为$k$方案总数。

      首先,显然即将添加的数$a_i$一定比之前添加的所有数要大,所以对于每一个没有完全划分好的集合都有贡献。首先计算当前差值的和是多少,我们会向每一个没有划分好的集合中添加一个差值$a_i - a_{i-1}$ , 所以当前差值的和就是$k + (a_i - a_{i-1}) imes j$.

      这是因为每一组的最值差就是这一组当中最大值和最小值之间所有数的差的和 。

      所以这$j$组由于没有被划分好,就需要向里面添加这个差值$a_i - a_{i-1}$ 

      考虑新添加这个元素作为一个新的分组的开始:$f_{i,j+1,v} += f_{i-1,j,k}$

      考虑新添加的这个元素作为一个新的分组的开始和结束:$f_{i,j,v} += f_{i-1,j,k}$

      考虑新添加的元素作为之前一个旧的还没划分好的组的非结束的元素:$f_{i,j,v} += f_{i-1,j,k} imes j$ ($j eq  0 $)

      考虑新添加的元素作为之前一个旧的还没划分好的组的结束元素$f_{i,j-1,v} += f_{i-1,j,k} imes j$ ($j eq  0 $)

      注意到这里枚举的状态$k$表示的是由那一维状态转移过来的状态$k$

      用滚动数组实现就可以排除空间限制。

      复杂度$O(n^2k)$

    # pragma GCC optimize(3)
    # include <bits/stdc++.h>
    # define int long long
    using namespace std;
    const int mo=1e9+7; 
    int f[2][205][1005];
    int a[205];
    int n,w;
    signed main()
    {
        scanf("%lld%lld",&n,&w);
        for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
        sort(a+1,a+1+n);
        int p=0; f[p][0][0]=1; 
        for (int i=1;i<=n;i++) {
            p^=1; memset(f[p],0,sizeof(f[p]));
            for (int j=0;j<=i;j++)
                for (int k=0;k<=w;k++) {
                    int v=(a[i]-a[i-1])*j+k;
                    if (v>w) continue;
                    f[p][j+1][v]+=f[p^1][j][k]; f[p][j+1][v]%=mo;
                    f[p][j][v]+=f[p^1][j][k];f[p][j][v]%=mo;
                    if (j) {
                        f[p][j][v]+=f[p^1][j][k]*j; f[p][j][v]%=mo;
                        f[p][j-1][v]+=f[p^1][j][k]*j;f[p][j-1][v]%=mo;
                    } 
                }   
        }
        int ret=0;
          for (int k=0;k<=w;k++)
           ret+=f[p][0][k],ret%=mo;
        printf("%lld
    ",ret);   
        return 0;
    }
    grp.cpp
  • 相关阅读:
    Mysql初始化root密码和允许远程访问
    windows下nodejs express安装及入门网站,视频资料,开源项目介绍
    python3.4学习笔记(二十六) Python 输出json到文件,让json.dumps输出中文 实例代码
    python3.4学习笔记(二十五) Python 调用mysql redis实例代码
    python3.4学习笔记(二十四) Python pycharm window安装redis MySQL-python相关方法
    python3.4学习笔记(二十三) Python调用淘宝IP库获取IP归属地返回省市运营商实例代码
    python3.4学习笔记(二十二) python 在字符串里面插入指定分割符,将list中的字符转为数字
    python3.4学习笔记(二十一) python实现指定字符串补全空格、前面填充0的方法
    python3.4学习笔记(二十) python strip()函数 去空格 函数的用法
    python3.4学习笔记(十九) 同一台机器同时安装 python2.7 和 python3.4的解决方法
  • 原文地址:https://www.cnblogs.com/ljc20020730/p/11170096.html
Copyright © 2011-2022 走看看