zoukankan      html  css  js  c++  java
  • 7.13模拟赛

    要活下去,总有一天我们能笑着缅怀过去的艰辛。


    T1.zoo

    找最小值的最大值的和,很绕口,但是这个让我联想起了货车运输,正是求图上两点路径最小值的最大值,做法自然是用最大生成树上找最小边的方法去卡这个限制。

    那就好说了,按两点最小权值建边,建图,建树,然后呢???

    我这里写了50分的O(n^2)暴力,对每个点来一次dfs统计,走人了。

    正解提供了这样一种似曾相识的思路,学到了。

    考虑Kruskal中合并两个联通块的情况,由于边是从大往小枚举的,这条边一定是连接这两个联通块的“瓶颈”边,也就是最小边,所以答案就要加上2*siz[u]*siz[v]*w

    这就完啦,学会了一种神奇的思路。

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    
    inline int rd(){
        int ret=0,f=1;char c;
        while(c=getchar(),!isdigit(c))f=c=='-'?-1:1;
        while(isdigit(c))ret=ret*10+c-'0',c=getchar();
        return ret*f;
    }
    
    const int MAXN=1000005;
    
    struct Edge{
        int from,to;
      long long w;
    }e[MAXN<<1];
    int ecnt;
    inline void add(int x,int y,int w){
        e[++ecnt].from = x;
        e[ecnt].to = y;
        e[ecnt].w = w;
    }
    
    int n,m;
    long long val[MAXN];
    
    int fa[MAXN];
    int fnd(int x){
        return x==fa[x]?x:fa[x]=fnd(fa[x]);
    }
    void cat(int x,int y){
        fa[y]=x;
    }
    bool cmp(const Edge &x,const Edge &y){
        return x.w>y.w;
    }
    
    unsigned long long siz[MAXN];
    unsigned long long ans;
    int main(){
        n=rd();m=rd();
        for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1,val[i]=rd();
        int x,y,w;
        for(int i=1;i<=m;i++){
            x=rd();y=rd();w=min(val[x],val[y]);
            add(x,y,w);
        }
        sort(e+1,e+1+ecnt,cmp);
        int cnt=0,nx,ny;
        for(int i=1;i<=ecnt;i++){
            x=e[i].from,y=e[i].to;w=e[i].w;
            nx=fnd(x),ny=fnd(y);
            if(nx==ny) continue;
        ans+=siz[nx]*siz[ny]*1ll*e[i].w;
        siz[nx]+=siz[ny];
            cat(nx,ny);
            if(++cnt==n-1) break;
        }
        cout<<(ans<<1);
        return 0;
    }
    View Code

    T2.segment

    考场没什么思路,磕了半天第三题,十分钟写的30分O(n^2)暴力

    正解是这样考虑区间覆盖的,对于一个区间[u,v],假设[l,r]能覆盖它,那必定有l<=u&&v<=r

    那么我们可以开两个树状数组,分别记录左端点和右端点,这两个树状数组以权值为下表,可以方便地查出前缀和

    两个前缀和相减就是答案啦

    对于删除操作,只需要把对应位置-1。

    这根本不segment tree...考场上第一反应居然是主席树版李超线段树?啥东西..

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    
    using namespace std;
    
    const int MAXN=1000005;
    
    int n,T;
    
    inline int rd(){
      int ret=0,f=1;char c;
      while(c=getchar(),!isdigit(c))f=c=='-'?-1:1;
      while(isdigit(c))ret=ret*10+c-'0',c=getchar();
      return ret*f;
    }
    
    struct BIT{
      int t[MAXN];
      void init(){memset(t,0,sizeof(t));}
      inline void update(int x,int w){for(int i=x;i<=n;i+=i&-i) t[i]+=w;}
      inline int query(int x){int ret=0;for(int i=x;i;i-=i&-i)ret+=t[i];return ret;}
    };
    
    int savx[MAXN],savy[MAXN],opt[MAXN];
    int bx[MAXN],by[MAXN];
    
    BIT L,R;
    int id[MAXN];
    
    void solve(){
      L.init();R.init();
      memset(id,0,sizeof(id));
      n=rd();
      int sum=0,tmp;
      for(int i=1;i<=n;i++){
          opt[i]=rd();
        if(!opt[i]){bx[i]=savx[i]=rd();by[i]=savy[i]=bx[i]+(++sum);id[sum]=i;}
        else{tmp=rd();tmp=id[tmp];bx[i]=savx[i]=savx[tmp];by[i]=savy[i]=savy[tmp];}
      }
      sort(bx+1,bx+1+n);sort(by+1,by+1+n);
      int totx=unique(bx+1,bx+1+n)-bx,toty=unique(by+1,by+1+n)-by;
      for(int i=1;i<=n;i++){
        savx[i]=lower_bound(bx+1,bx+1+totx,savx[i])-bx;//%%%HSZ
        savy[i]=lower_bound(by+1,by+1+toty,savy[i])-by;
      }
      for(int i=1;i<=n;i++){
        if(opt[i]){L.update(savx[i],-1);R.update(savy[i],-1);continue;}
        printf("%d
    ",R.query(savy[i])-L.query(savx[i]-1));
        L.update(savx[i],1);R.update(savy[i],1);
      }
    
    }
    
    int main(){
      T=rd();int t=T;
      while(T--) printf("Case #%d:
    ",t-T),solve();
      return 0;
    }
    View Code

    T3.number

    考虑这样一个事情,k严格小于max(n,m),意味着,不存在一行或者一列,使得该行或该列全部被预先填好

    也就是说,每一行、列都会有至少一个空着的,可以考虑这样一个事情,在k=0的情况下:

    对于第一列,共有n个元素,要填入奇数个-1,有C(n,1)+C(n,3)+C(n,5)+...种填法,SPLI学长讲过,这货就等于2^(n-1)

    同理,每列亦如此,答案就是(n-1)*(m-1),因为有一列是空出来的。

    在考虑k的情况下,会发现,填入一个 k,答案就会除以二,所以答案就是(n-1)*(m-1)-k。

    为什么这样对呢?因为之前说过,k严格小于max(n,m)

    注意几个事情,当k>(n-1)*(m-1)时,要取0,也就是答案是pow(2,max(0,(n-1)*(m-1)-k)),以及当(n+m)为奇数,也就是(n&1)!=(m&1)时

    考试的时候推出来式子,怕不稳就对着30写了个状压,大概这样

    f[i][j]表示考虑到第i行,每列在前i行共填入了奇数/偶数个-1,用0和1表示

    考虑转移,枚举i-1行和i行的状态j和k,填写了-1的位置就是v=j^k,判断这个v中是否是奇数个1(满足每行的限制),即可转移

    问题是没仔细考虑空间!MLE

    吸取教训,吸取教训。(其实写的那个式子没考虑和0取max,测出来也只有60分罢了)

    #include<iostream>
    #include<cstdio>
    
    using namespace std;
    
    int n,m,k,MOD;
    
    typedef long long ll;
    
    ll qpow(ll x,ll y){
      ll ret=1,base=x;
      while(y){
        if(y&1)(ret*=base)%=MOD;
        (base*=base)%=MOD;
        y>>=1ll;
      }
      return ret;
    }
    
    int main(){
      cin>>n>>m>>k;
      if((n&1)!=(m&1)) return puts("0"),0;
      int x,y,w;
      for(int i=1;i<=k;i++) cin>>x>>y>>w;
      cin>>MOD;
      cout<<qpow(2ll,max(0ll,1ll*(n-1)*(m-1)-k));
      return 0;
    }
    View Code

    说起来,全场可能就我不知道发了两个大样例(虽然后来证明没什么用..)


     https://files.cnblogs.com/files/ghostcai/7.13.pdf

    本文来自博客园,作者:GhostCai,转载请注明原文链接:https://www.cnblogs.com/ghostcai/p/9306712.html

  • 相关阅读:
    编译器是C写的,包括一点C++,editor和debugger是C++写的(最早的16位编译器是纯汇编写的)
    2016 年美国大选,特朗普赢在哪儿?
    特朗普上台的原因:是经济全球化被甩出去人的反扑。但互联网时代不可阻挡,必须参与。开历史倒车是没用的,就像欧洲的羊吃人时代是一样的。信仰基督教以及美国传统价值观的那群老人,终归是要死去。
    Java 开发规约插件
    c#.net公共帮助类
    elasticsearch集群及filebeat server和logstash server
    NopCommerce源码架构
    局部函数
    C#实现人脸识别
    NET Core
  • 原文地址:https://www.cnblogs.com/ghostcai/p/9306712.html
Copyright © 2011-2022 走看看