zoukankan      html  css  js  c++  java
  • 2020牛客寒假算法基础集训营6

    https://ac.nowcoder.com/acm/contest/3007#question

    前两天打的最后一场,过的题最少的一场......正好碰了盲区加状态差,也问题不大。

    md,刚刚在门口吃东西撞了玻璃门,镜框坏了,被迫出门上街换了镜框。。。。。我真傻,真的。撞玻璃门是真实存在的。

    然后补题记录思维。

    A.配对  好像是个啥不等式,以前上课老师讲过,感觉差不多。排序后小的和大的组合一下即可。

    G.号序列 跟前一天的差不多,模拟配对即可。

    J.签到题 冷静推式子...话说我也推了半个小时???真的一点也不冷静其实我。

    ---------------然后就陷入自闭-----------

    F.十字阵列 这个F第一次看样例根本看不懂,后来才知道要求的是wij*(i+j) 。加个乘号真的更清晰好吗。不然   光一个 Σwij(i+j) 感觉真的8太行

    反正我有问题,看了好久加好几次通知才看出来。看懂了就算一下贡献就过了。

    D.重排列 

    开始只知道求A的重排列,那对A排序肯定不影响,没发现A的任意排列,也可以将B对应A的排列给找到,所以对B排序也不会影响。

    然后对A对B都排个序,然后找每位 从A中能填多少种数,就是相应的B里二分查找一下有多少个A中的数小于等于B当前这个数,用upper_bound找一下,同时减去已经被前位占的数i,即可。

    B. 

    完全没听说过的基环树,基环内向树,处理环的经验相对欠缺,做不来。比赛的时候并未深入思考。看了代码发现也还行叭。就遍历过程中dfs时候优先处理环的答案,先把环的答案全处理出来,由于内向树,出度为1,用while就可以了。先把内部搞定,然后外部就是逐层加1了。然后标程处理环的时候模拟了栈,感觉还挺麻烦的。

    参考别人的这份代码还不错,就理解下dfs到环,区分已经vis,ans到的答案怎么更新即可,遇到环就已经把这个环的答案全部记录上。

     1 #include <bits/stdc++.h>
     2 #ifndef ONLINE_JUDGE
     3 #define debug(x) cout << #x << ": " << x << endl
     4 #else
     5 #define debug(x)
     6 #endif
     7 using namespace std;
     8 typedef long long ll;
     9 const int maxn=1e6+7;
    10 const int inf=0x3f3f3f3f;
    11 const int mod=1e9+7;
    12  
    13 int vis[maxn];
    14 int to[maxn];
    15 int ans[maxn];
    16 int res;
    17  
    18 void dfs(int x)
    19 {
    20     vis[x]=1;
    21     if(vis[to[x]])
    22     {
    23         if(!ans[to[x]])
    24         {
    25             int now=x,num=1;
    26             while(to[now]!=x)
    27             {
    28                 now=to[now];
    29                 num++;
    30             }
    31             now=x;
    32             ans[x]=num;
    33             while(to[now]!=x)
    34             {
    35                 now=to[now];
    36                 ans[now]=num;
    37             }
    38             res=max(res,num);
    39         }
    40         else
    41             ans[x]=ans[to[x]]+1;
    42     }
    43     else
    44     {
    45         dfs(to[x]);
    46         if(!ans[x]) ans[x]=ans[to[x]]+1;
    47     }
    48     res=max(res,ans[x]);
    49 }
    50  
    51 int main()
    52 {
    53     ios::sync_with_stdio(false);
    54     cin.tie(0);
    55     int n;
    56     cin>>n;
    57     for(int i=1,x;i<=n;++i)
    58     {
    59         cin>>x;
    60         to[i]=x;
    61     }
    62     for(int i=1;i<=n;++i)
    63     {
    64         if(!vis[i]) dfs(i);
    65     }
    66     cout<<res<<endl;
    67     return 0;
    68 }

    基环树优先考虑处理环。

     

    C.汉诺塔 

    Dilworth定理比赛里我是真的想到了啥啥定理来着,前几天CF好像碰到过,想到了就是求最小升序划分就最长降序子序列(反正两个形容词都反一下就对了)。

    然后我不会nlog的子序列求法,LIS没学好,其实是大概有印象的,维护一下新数组,二分去替换更新最优的答案。这题我是把x按从大到小排序的,然后就是求y同样降序的最小划分,也就是求y的最长上升子序列了。这样排序处理可以自己设计一下,更方便。

    然后就是LIS的事情了,每次二分找,如果在新数组中找不到比当前这个y大的,(比当前大说明可以替换,就是扩充这一组,把大的换成更小的,保证了x更小,y更小),找不到,就新增一组,同时组号就是在新数组中的下标。

    由于这里保证没有相同的x和y,所以upper_bound和lower一样可以。

    #include <bits/stdc++.h>
    #ifndef ONLINE_JUDGE
    #define debug(x) cout << #x << ": " << x << endl
    #else
    #define debug(x)
    #endif
    using namespace std;
    typedef long long ll;
    const int maxn=2e5+7;
    const int inf=0x3f3f3f3f;
    const int mod=1e9+7;
     
    struct node
    {
        int x,y,id;
    }a[maxn];
    bool cmp(node a,node b)
    {
        return a.x>b.x;
    }
    int n,cnt,st[maxn],ans[maxn];
     
    int main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);
        int n;
        cin>>n;
        for(int i=1;i<=n;++i)
        {
            cin>>a[i].x>>a[i].y;
            a[i].id=i;
        }
        sort(a+1,a+1+n,cmp);
        for(int i=1;i<=n;++i)
        {
            int pos=upper_bound(st+1,st+1+cnt,a[i].y)-st;
            st[pos]=a[i].y;
            if(pos>cnt) cnt=pos;
            ans[a[i].id]=pos;
        }
        cout<<cnt<<endl;
        for(int i=1;i<=n;++i)
            cout<<ans[i]<<' ';
        cout<<endl;
        return 

    E.立方数 

    这题也挺搞,看了题面,又看到群里有人说Pollard_rho 然后我刚好接触过,(仅仅接触过,没完全懂)然后疯狂找板子改,然后疯狂T,其实这个大数质因子分解的复杂度是O(N^1/4)据说,然后T*(N^1/4)高达3e8过不去也正常,况且分解完map统计可能又会变大?

    题解给的做法又是要脑子的。因为要找最大的a,使得a^3*b=n嘛。a最大也就1e6啊,显然可以筛1e6内素数,然后就有T*(n^1/3)/(ln(n^1/3))  后面那个为素数密度。。。反正还是很大。

    然后脑子转下?就有了筛n^1/4内素数,n^1/4内素数全部筛掉并统计三次方进答案,最后剩下的数要么是n^1/4以上数的三次方,要么对答案无贡献为啥呢?

    假设如果剩下的数是(n^1/4以上的质数)^3 *b(b>1),那么b要么应该在前面被筛掉,要么应该大于n^1/4,乘起来反而大于n,显然矛盾了。

    然后就证明完了,那么完了,脑子该去哪找?

    所以记录下思维吧,其实先常规思考,最后尝试往更小范围的质数想,降低复杂度。

    最后也可以二分,只是check嘛,方法很多。

     1 #include <bits/stdc++.h>
     2 #ifndef ONLINE_JUDGE
     3 #define debug(x) cout << #x << ": " << x << endl
     4 #else
     5 #define debug(x)
     6 #endif
     7 using namespace std;
     8 typedef long long ll;
     9 const int maxn=31700;
    10 const int inf=0x3f3f3f3f;
    11 const int mod=1e9+7;
    12 
    13 int vis[maxn];
    14 vector<int>pri;
    15 vector<ll> ptri;
    16 int main()
    17 {
    18     for(int i=2;i<maxn;++i)
    19     {
    20         if(!vis[i])
    21         {
    22             pri.push_back(i);
    23             ptri.push_back(1ll*i*i*i);
    24             for(int j=i*i;j<maxn;j+=i)
    25                 vis[j]=1;
    26         }
    27     }
    28     int t;
    29     scanf("%d",&t);
    30     while(t--)
    31     {
    32         ll n;
    33         scanf("%lld",&n);
    34         int ans=1;
    35         for(int i=0;i<pri.size();++i)
    36         {
    37             if(n%pri[i]==0)
    38             {
    39                 while(n%ptri[i]==0)
    40                 {
    41                     ans*=pri[i];
    42                     n/=ptri[i];
    43                 }
    44                 while(n%pri[i]==0)
    45                     n/=pri[i];
    46             }
    47         }
    48         int t=pow(n,1.0/3);
    49         for(int i=max(1,t);i<=t+1;++i)
    50             if(1ll*i*i*i==n) {ans*=i;break;}
    51         printf("%d
    ",ans);
    52     }
    53     return 0;
    54 }

    I.导航系统

    n=500,可为啥好多n^3代码都能过呢,1s能跑1e8多吗?我迷惑啦。n^3确实好理解。

    就是对每个点,肯定至少有一条和其他点直接相连的边,然后考虑最小生成树的过程,这个直接相连的边肯定是这两个点之间的最短距离没毛病,通过最小生成树找到这n-1条边。

    然后通过n-1条边还原出原图的最短距离,再和原图比较,这是我看了别人代码和题解的理解。然后我也n^3 floyd了,毕竟好理解,也能过。。。

     1 #include <bits/stdc++.h>
     2 #ifndef ONLINE_JUDGE
     3 #define debug(x) cout << #x << ": " << x << endl
     4 #else
     5 #define debug(x)
     6 #endif
     7 using namespace std;
     8 typedef long long ll;
     9 const int maxn=3e5+7;
    10 const int inf=0x3f3f3f3f;
    11 const int mod=1e9+7;
    12  
    13 ll mp[505][505];
    14 ll d[505][505];
    15 int ans[505],fa[505];
    16 struct edge
    17 {
    18     int u,v,w;
    19 }e[maxn];
    20 int tot;
    21 void addedge(int u,int v,int w)
    22 {
    23     e[tot].u=u;
    24     e[tot].v=v;
    25     e[tot++].w=w;
    26 }
    27 bool cmp(edge a,edge b) {return a.w<b.w;}
    28  
    29 int find(int x)
    30 {
    31     if(fa[x]==-1) return x;
    32     return fa[x]=find(fa[x]);
    33 }
    34  
    35 int main()
    36 {
    37     ios::sync_with_stdio(false);
    38     cin.tie(0);
    39     int n;
    40     cin>>n;
    41     for(int i=1;i<=n;++i)
    42     {
    43         for(int j=1;j<=n;++j)
    44         {
    45             cin>>mp[i][j];
    46             if(i!=j) addedge(i,j,mp[i][j]);
    47         }
    48     }
    49     sort(e,e+tot,cmp);
    50     memset(fa,-1,sizeof(fa));
    51     for(int i=1;i<=n;++i)
    52         for(int j=1;j<=n;++j)
    53             if(i==j) d[i][j]=0;
    54             else d[i][j]=1e15;
    55     int cnt=0;
    56     for(int i=0;i<tot;++i)
    57     {
    58         int u=e[i].u,v=e[i].v,w=e[i].w;
    59         int t1=find(u),t2=find(v);
    60         if(t1!=t2)
    61         {
    62             fa[t1]=t2;
    63             ans[cnt++]=w;
    64             d[u][v]=d[v][u]=w;
    65         }
    66     }
    67     for(int k=1;k<=n;++k)
    68         for(int i=1;i<=n;++i)
    69             for(int j=1;j<=n;++j)
    70                 d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
    71     for(int i=1;i<=n;++i)
    72         for(int j=1;j<=n;++j)
    73             if(d[i][j]!=mp[i][j])
    74                 {cout<<"No
    ";return 0;}
    75     cout<<"Yes
    ";
    76     for(int i=0;i<cnt;++i) cout<<ans[i]<<'
    ';
    77     return 0;
    78 }

    H.

    神奇的扫描线。观察发现(发现不了) 处理相对运动好吧。。。又是物理。。。题解讲的还行,看代码的时候主要不知道投影坐标怎么算的。

    然后发现列个式子就出来了,一个y=x+q-p,与y=-x联立,就可以解得,然后等比例放大并且横一下就可以,直接简化为  纵-横。

    同时每个矩形投影为2个点,有左有右,左是标记开始统计这个,右是结束这个的统计。方便设为1,和-1,两个象限分别种类为0和1.

    然后排序扫描线,对于每个是进入的矩形左端点,加上   在他之前进入并且没有出去的另一种   就是答案,保证没有重复,因为 我统计的是之前进入的另一个种类 对当前的影响,非常的神奇,扫描一遍就出来了。然后有个细节就是位置相同的,左端点先进入,种类倒无所谓,排序的时候注意处理一下即可。

     1 #include <bits/stdc++.h>
     2 #ifndef ONLINE_JUDGE
     3 #define debug(x) cout << #x << ": " << x << endl
     4 #else
     5 #define debug(x)
     6 #endif
     7 using namespace std;
     8 typedef long long ll;
     9 const int maxn=4e5+7;
    10 const int inf=0x3f3f3f3f;
    11 const int mod=1e9+7;
    12 
    13 struct node
    14 {
    15     int pos,lr,type;
    16 }e[maxn];
    17 
    18 bool cmp(node a,node b)
    19 {
    20     return a.pos<b.pos || a.pos==b.pos && a.lr>b.lr;
    21 }
    22 ll cnt[2];
    23 
    24 int main()
    25 {
    26     int n,m,x,y,p,q,k=0;
    27     scanf("%d%d",&n,&m);
    28     for(int i=0;i<n;++i)
    29     {
    30         scanf("%d%d%d%d",&x,&y,&p,&q);
    31         e[k++]={q-p,1,0};
    32         e[k++]={y-x,-1,0};
    33     }
    34     for(int i=0;i<m;++i)
    35     {
    36         scanf("%d%d%d%d",&x,&y,&p,&q);
    37         e[k++]={q-p,1,1};
    38         e[k++]={y-x,-1,1};
    39     }
    40     sort(e,e+k,cmp);
    41     ll ans=0;
    42     for(int i=0;i<k;++i)
    43     {
    44         cnt[e[i].type]+=e[i].lr;
    45         if(e[i].lr==1)
    46             ans+=cnt[e[i].type^1];
    47     }
    48     printf("%lld
    ",ans);
    49     return 0;
    50 }

    然后这6套题差不多补完了,补了58道吧?除了5的大模拟和4的没懂的计数。至少要么比赛里出了,要么赛后看题解看好多份别人代码 加 自己写一遍甚至二种写法练下了。

    补的还算细吧,希望能记住啦啦啦啦啦 。逃~

    哪里不足或者迷惑,评论区可以交流。等一手互动。

  • 相关阅读:
    搭建kafka集群
    fluentd 安装、配置、使用介绍
    彻底解决 es 的 unassigned shards 症状
    nginx 反向代理时丢失端口的解决方案
    kubernetes的imagePullSecrets如何生成及使用
    创建MySQL数据库账号
    Linux中查找文件
    Linux快速访问多个目录
    Django查询数据库返回字典dict数据
    linux 将压缩包复制到另外一个文件夹下面
  • 原文地址:https://www.cnblogs.com/Zzqf/p/12327340.html
Copyright © 2011-2022 走看看