zoukankan      html  css  js  c++  java
  • 2021.2 月赛补题A-C

    2021.2 月赛补题A-C

    我一直觉得EOJ的题解写的可以,所以思路和题解基本参考Eoj官方题解。

    连接:https://acm.ecnu.edu.cn/blog/entry/1089/

    这场真不愧是oi爷出题,我还没摸键盘,只是在地铁上看了看A,知道是个lca,没有想出来dfs序,就开始自闭了。

    但我觉得这场题目很好,值得一写。

    A.
    /*
      首先要知道两个结论:
      1.就是n个点的lca就是dfs序差最大的一组数的lca,所以先要对这5个点进行dfs序排序,求出5个点的lca
      2.其余的点u对已经有的点合并:u对已存在的点求lca,深度最深的lca就是离这个点u最近的公共节点
      复杂度O(10qlogn)
    */
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define debug(x) cout<<#x<<':'<<x<<endl;
    #define u first
    #define w second
    const int maxn=5e4+60;
    int n,q,u,v,f,cnt;
    int father[maxn][32],cost[maxn][32],depth[maxn],a[6],dis[maxn],ls[maxn],rs[maxn];
    vector<pair<int,int>>G[maxn];
    void dfs(int root,int b){
        father[root][0]=b;ls[root]=++cnt;
        depth[root]=depth[father[root][0]]+1;
        for(int i=1;i<32;i++){
            father[root][i]=father[father[root][i-1]][i-1];
        }
        int sz=G[root].size();
        for(int i=0;i<sz;i++){
            if(G[root][i].u==b) continue;
            dis[G[root][i].u]=dis[root]+G[root][i].w;
            dfs(G[root][i].u,root);
        }
        rs[root]=cnt;
    }
    int lca(int x,int y){
        if(depth[x]<depth[y]) swap(x,y);
        for(int i=31;i>=0;i--){//无限逼近接近二分,把x,y调整到同一深度
            if(depth[father[x][i]]>=depth[y])x=father[x][i];
        }
        if(x==y) return x;
        for(int i=31;i>=0;i--){
            if(father[x][i]!=father[y][i]){
                x=father[x][i];
                y=father[y][i];
            }
        }
        return father[x][0];
    }
    bool cmp(int a,int b){
        return ls[a]<ls[b];
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++) G[i].clear();
        for(int i=1;i<n;i++){
            scanf("%d%d%d",&u,&v,&f);u++,v++;//确保节点数从1开始
            G[v].push_back({u,f}); G[u].push_back({v,f});
        }
        dfs(1,0);
        scanf("%d",&q);
        for(int i=1;i<=q;i++){
            for(int j=1;j<=5;j++)
            scanf("%d",&a[j]),a[j]++;
            sort(a+1,a+6,cmp);
            
            int qwq=lca(a[1],a[5]);
            int ans=dis[a[1]]+dis[a[5]]-2*dis[qwq];
    
            swap(a[2],a[5]);
            for(int j=3;j<=5;j++){//并到之前的点上
                int bst_lca=0;
                for(int k=1;k<j;++k){
                    int now_lca=lca(a[j],a[k]);
                    if(bst_lca==0||depth[now_lca]>depth[bst_lca]){//找一个深度最深的lca
                        bst_lca=now_lca;
                    }
                }
                ans+=dis[a[j]]-dis[bst_lca];
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
    
    B.
    /*
    对每个人起终点bfs求单源最短路,然后算通过每点的概率
    pre数组表示到该点最短路的数量,1表示从起点出发,0表示从终点出发
    复杂度O(n*k)
    */
    
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn=1e5+100;
    const int inf =0x3f3f3f3f;
    double sum[maxn];
    vector<int>G[maxn];
    int n,m,u,v,k;
    int a[maxn],b[maxn],pre[maxn][2],d[maxn][2],vis[maxn];
    #define debug(x) cout<<#x<<':'<<x<<endl;
    void bfs(int s,int f){
        queue<int>q;
        for(int i=1;i<=n;i++) vis[i]=0,d[i][f]=inf;
        q.push(s);d[s][f]=0;pre[s][f]=1;
        while(!q.empty()){
            int u=q.front();
            q.pop();
            if(vis[u]) continue;
            else vis[u]=1;
    
            for(auto v: G[u]){
                if(vis[v]) continue;
                if(d[v][f]>d[u][f]+1){
                    d[v][f]=d[u][f]+1;
                    pre[v][f]=pre[u][f];
                    q.push(v);
                }else if(d[v][f]==d[u][f]+1){
                    pre[v][f]+=pre[u][f];
                }
            }
        }
    }
    int main(){
        scanf("%d%d",&n,&m);
        while(m--){
            scanf("%d%d",&u,&v);u++,v++;
            G[u].push_back(v);
            G[v].push_back(u);
        }
        scanf("%d",&k);
        for(int i=1;i<=k;i++){
            scanf("%d%d",&a[i],&b[i]);a[i]++,b[i]++;
            for(int i=1;i<=n;i++) pre[i][1]=pre[i][0];
            bfs(a[i],1),bfs(b[i],0);
            for(int j=1;j<=n;j++){
                if(j==a[i]||j==b[i]) sum[j]+=1;//起点和终点的概率是100%
                /*经过该点的概率=起点到该点的方法数*该点到终点的方法数/起点到终点的方法数*/
                else if(d[j][1]+d[j][0]==d[b[i]][1]) sum[j]+=1.0*pre[j][1]*pre[j][0]/pre[b[i]][1];
            }
        }
        int ans=1;
        double dmax=0;
        for(int i=1;i<=n;i++){
            if(sum[i]>dmax)dmax=sum[i],ans=i;
        }
        printf("%d",ans-1);
    }
    
    
    C.
    /*
    这题我一开始就觉得是一道lazy线段树,但以下的代码是根据EOJ说的题解补的,我觉得这个写法更简单
    核心思想是:牢牢把握住每段区间最小的值,也就意味着对于当前这个区间[mini[w].first,mini[w].second]必然有i,
    而绝没有比它小的数,那就意味着可以随意取这个区间之内的一个位置来放这个值
    */
    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=2e5+100;
    int ans[maxn],vis[maxn],n,q;
    //#define debug(x) cout<<#x<<':'<<x<<endl;
    pair<int,int>mini[maxn],maxi[maxn];
    
    void deal(int n) {
        for (int i = 1; i <= n; ++i) cout << -1 << ' ';
        cout << endl;
        exit(0);//这个写法很好,很干净,值得借鉴
    }
    int main(){
        set<int>s;
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++){
            mini[i]={1,n},maxi[i]={n,1};
            s.insert(i);
        }
        while(q--){
            int l,r,w;
            scanf("%d%d%d",&l,&r,&w);l++,r++,w++;
            mini[w].first=max(mini[w].first,l);
            mini[w].second=min(mini[w].second,r);
            maxi[w].first=min(maxi[w].first,l);
            maxi[w].second=max(maxi[w].second,r);
            vis[w]=1;
        }
        queue<int>Q;
        for(int i=n;i>=1;i--){//枚举值找位置
            if(!vis[i])Q.push(i);
            else{
                /* 关键在于 ,[mini[w].first,min[w].second]区间里绝不可能有比他更小的值*/
                auto to=s.upper_bound(mini[i].second);--to;
                //if (to == s.end() or *to != mini[i].second) --to;//没找到
                if (*to < mini[i].first)deal(n);//在set中找不到符合要求的位置
                ans[*to] = i;//否则这个位置是值,因为可以随意指定,
                s.erase(to);
                int l = maxi[i].first, r = maxi[i].second;//对于这个区间中剩下的位置把比当前的数大的数安排进去
                auto st=s.lower_bound(l),ed = s.upper_bound(r);
                /* 这里值得注意的是,这些位置的数必然是大于当前的i,如果位置多于值,就说明构造不出来*/
                for (auto it = st; it != ed; ++it) {//枚举这个位置区间,把值安排进这个区间里
                    if ((int)Q.size()==0) deal(n);
                    ans[*it] = Q.front(), Q.pop();
                }
                s.erase(st, ed);
            }
        }
        
       for (auto to : s) {//把Q中剩余的数安排去处//非常容易忽视的细节,一定要写
            if ((int)Q.size()==0){ deal(n);}
            ans[to] = Q.front();Q.pop();
        }
        
        for (int i = 1; i <= n; ++i) {
            cout << ans[i] - 1 << " ";
        }
        
        //system("pause");
        return 0;
    }
    
  • 相关阅读:
    WPF 使用 Direct2D1 画图 绘制基本图形
    WPF 使用 Direct2D1 画图 绘制基本图形
    dot net core 使用 IPC 进程通信
    dot net core 使用 IPC 进程通信
    win2d 图片水印
    win2d 图片水印
    Java实现 LeetCode 240 搜索二维矩阵 II(二)
    PHP closedir() 函数
    PHP chroot() 函数
    PHP chdir() 函数
  • 原文地址:https://www.cnblogs.com/zx0710/p/14387014.html
Copyright © 2011-2022 走看看