zoukankan      html  css  js  c++  java
  • 【考试反思】联赛模拟测试13

    震惊!Midoria7 居然是个 nt。为什么呢?快和小编一起来看一看吧~

    震惊!Midoria7 竟然直接把正解注释掉,活到爆。


    T1: 100 ( ightarrow) 5

    T2: 90 ( ightarrow) 50

    T3: 50 ( ightarrow) 0

    T4: 20 ( ightarrow) 0

    T1:Tree

    直接输出边权和。

    震惊,所以 512 MB2000 ms 都是骗人的。

    个人理解:肯定会有一种情况使排列中相邻两数都直接相连答案就取该边边权。如果你用换一种排列,一定会使某些更小的边被更多次取到。

    T2:Permutation

    直接冒泡 50 pts,然而似乎剪个枝就 90 pts 了。数据居然折磨水。

    乱搞的 90 pts Code
    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=5e5+10;
    int n,K;
    int a[maxn],pos[maxn];
    
    inline int read(){
        int x=0;bool fopt=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
        for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
        return fopt?x:-x;
    }
    
    inline void Swap(int &x,int &y){
        int temp=x;x=y;y=temp;
    }
    
    int main(){
    #ifndef LOCAL
        freopen("permutation.in","r",stdin);
        freopen("permutation.out","w",stdout);
    #endif
        n=read();K=read();
        for(int i=1;i<=n;i++){
            a[i]=read();
            pos[a[i]]=i;
        }
        for(register int T=1;T<=n;T++){
            int flag=0;
            for(register int i=1;i<=n;i++){
                int now=a[i];
                if(now!=1&&pos[now-1]-pos[now]>=K){
                    Swap(a[i],a[pos[now-1]]);
                    Swap(pos[now],pos[now-1]);
                    flag=1;
                }
            }
            for(register int i=1;i<=n;i++){
                int now=a[i];
                if(now!=n&&pos[now]-pos[now+1]>=K){
                    Swap(a[i],a[pos[now+1]]);
                    Swap(pos[now],pos[now+1]);
                    flag=1;
                }
            }
            if(!flag)break;
        }
        for(int i=1;i<=n;i++)
            printf("%d
    ",a[i]);
        return 0;
    }
    

    正向字典序最小,等同于逆置换的逆向字典序最大。

    引用镇楼。逆置换就是数值和下标互换的序列。

    令原排列为 (P)(Q=P^{-1})。那么题目要求的条件就是 (vert Q_i-Q_{i+1}vertgeq K)

    字典序最小,就是 (Q_1) 尽量小,其次 (Q_2) 尽量小,以此类推。

    注意到,对于一对 (i)(i+1),如果其 (Q) 差值小于 (K),那他们就永远无法交换。即对于 (Q) 的相邻位,若差值小于 (K),那就永远无法跨越。

    那对于两个下标 (i,j) 来说,若 (i<j),且 (vert Q_i-Q_jvert< K),我们就建上边,表示一个不等式条件。整个序列变成了一个 DAG,那么我们用一个拓扑序,满足字典序最小。首先我们肯定不能用普通队列,而是优先队列(见菜肴制作

    正确的操作是:反向建边,每次取最大编号,且拓扑编号从 (n)(1)。(但是目前并不会证为什么和正向拓扑有区别,反正引用了就是对的

    首先,这样建边是 (O(nK)) 的,时间和空间都会炸,显然不能真的建出图来。所以我们考虑用线段树优化。

    维护一个 (P) 序列区间最大值线段树,但线段树中记录的是 (Q)pushup维护一下即可。那么我们考虑入度为 (0) 的点满足什么条件。

    返回来看我们的建边条件。对于一个 (vert Q_i-Q_jvert< K) 的下标 ((i,j)),如果一开始 (P_i<P_j),那么最后肯定也是 (P_i<P_j),那么我们反向建图就建了一条 (j ightarrow i) 的边。

    那么对于一个下标 (i),如果 (P_i)(P) 序列区间 ([i-K,i+K]) 中的最大值,那么 (i) 一定是入度为 (0) 的点,因为没有比他更大的 (P_j) 了。那我们直接把它入队。

    拓扑排序的过程中,我们取出堆顶,然后删除。然后我们检查 ([i-K,i-1])([i+1,i+K]) 两个区间,如果入度为 (0),那么也一并入队即可(拓扑排序基本操作)

    最后输出答案。所以本题比较有趣的转化就是入度为 (0) 的点的判断。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=2e6+10;
    int n,K;
    int a[maxn],ans[maxn];
    
    inline int read(){
        int x=0;bool fopt=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
        for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
        return fopt?x:-x;
    }
    
    #define lson (rt<<1)
    #define rson (rt<<1|1)
    int tree[maxn<<2];
    inline void pushup(int rt){
        tree[rt]=a[tree[lson]]>a[tree[rson]]?tree[lson]:tree[rson];
    }
    
    void build(int rt,int l,int r){
        if(l==r)return tree[rt]=l,void();
        int mid=(l+r)>>1;
        build(lson,l,mid);build(rson,mid+1,r);
        pushup(rt);
    }
    
    void del(int rt,int l,int r,int p){
        if(l==r)return tree[rt]=0,void();
        int mid=(l+r)>>1;
        if(p<=mid)del(lson,l,mid,p);
        else del(rson,mid+1,r,p);
        pushup(rt);
    }
    
    int query(int rt,int l,int r,int s,int t){
        if(r<s||l>t)return 0;
        if(s<=l&&r<=t)return tree[rt];
        int mid=(l+r)>>1;
        int L=query(lson,l,mid,s,t),R=query(rson,mid+1,r,s,t);
        return a[L]>a[R]?L:R;//若有不合法区间会return 0,由于a[0]=-INF,所以一定不会被取到
    }
    
    bool vis[maxn];
    priority_queue<int> q;
    inline void Push(int u){
        if(vis[u])return;
        if(query(1,1,n,u-K+1,u+K-1)==u){
            q.push(u);vis[u]=1;
        }
    }
    
    int main(){
    #ifndef LOCAL
        freopen("permutation.in","r",stdin);
        freopen("permutation.out","w",stdout);
    #endif
        n=read();K=read();a[0]=-0x3f3f3f3f;
        for(int i=1;i<=n;i++)
            a[i]=read();
        build(1,1,n);
        for(int i=1;i<=n;i++)
            Push(i);
        for(int i=n;i>=1;i--){
            int u=q.top();q.pop();
            ans[u]=i;
            del(1,1,n,u);
            int pos=query(1,1,n,u-K+1,u-1);
            if(pos)Push(pos);
            pos=query(1,1,n,u+1,u+K-1);
            if(pos)Push(pos);
        }
        for(int i=1;i<=n;i++)
            printf("%d
    ",ans[i]);
        return 0;
    }
    

    T3:Graph

    震惊,打了 160 行部分分之后爆零,人傻了。

    原因是我没用统计方案个数而是耍聪明直接输出 (cfrac{m}{2})

    显然对于一个联通块非常正确,但是数据中居然有这样的数据,人傻了。

    正解基本就是树形态的改一改。我们先想树形态怎么做,显然从下到上,儿子先自己配对,多出来的单个的向上传递,和连父边配对。

    int dfs(int u,int fa){
        int now=0;
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].to;
            if(v==fa)continue;
            int temp=dfs(v,u);
            if(temp)ans.push_back((Node){temp,v,u});//有从向下传来的节点,先配对
            else{
                if(now){
                    ans.push_back((Node){now,u,v});//儿子自己互相配对
                    now=0;
                }else now=v;
            }
        }
        return now;//当now是0,表示该点的儿子都互相配对完了,否则就向上传递
    }
    

    所以显然对于每一个联通块都是有一个 dfs 树的。然而不同的就是多了一些非树边,但处理是一样的。我们只需判断一下深度,防止重复即可。

    显然对于 3 连接的非树边,我们要去处理 8 而不是 1...

    Code
    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=1e5+10;
    const int maxm=2e5+10;
    int n,m;
    
    struct Edge{
        int from,to,id,nxt;
    }e[maxm<<1];
    
    struct Node{
        int x,y,z;
    };
    
    inline int read(){
        int x=0;bool fopt=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
        for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
        return fopt?x:-x;
    }
    
    int head[maxn],cnt;
    inline void add(int u,int v){
        e[++cnt].from=u;
        e[cnt].to=v;
        e[cnt].nxt=head[u];
        head[u]=cnt;
    }
    
    int dep[maxn];
    vector<Node> ans;
    int dfs(int u){
        int now=0;
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].to;
            if(!dep[v]){
                dep[v]=dep[u]+1;
                int temp=dfs(v);
                if(temp)ans.push_back((Node){temp,v,u});
                else{
                    if(now){
                        ans.push_back((Node){now,u,v});
                        now=0;
                    }else now=v;
                }
            }else{
                if(dep[v]>dep[u]){
                    if(now){
                        ans.push_back((Node){now,u,v});
                        now=0;
                    }else now=v;
                }
            }
        }
        return now;
    }
    
    int main(){
    #ifndef LOCAL
        freopen("graph.in","r",stdin);
        freopen("graph.out","w",stdout);
    #endif
        n=read();m=read();
        for(int i=1;i<=m;i++){
            int u=read(),v=read();
            add(u,v);add(v,u);
        }
        for(int i=1;i<=n;i++)
            if(!dep[i]){
                dep[i]=1;dfs(i);
            }
        printf("%d
    ",ans.size());
        for(int i=0;i<ans.size();i++)
            printf("%d %d %d
    ",ans[i].x,ans[i].y,ans[i].z);
        return 0;
    }
    

    T4:十字绣

    似乎是原题,然而是小假期集训的没做过。


    震惊!sb Midoria7 居然不删调试,活到爆。

    格点连边,找联通块大小即可。注意边界。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=1e5+10;
    int n,m,ans;
    char s[maxn];
    
    struct Edge{
        int from,to,w,nxt;
    }e[maxn<<4];
    
    int cnt;
    int head[maxn],face[maxn],rear[maxn],f[maxn];
    inline int add(int u,int v,int w){
        e[++cnt].from=u;
        e[cnt].to=v;
        w==1?face[u]++:rear[u]++;
        f[u]=1;
        e[cnt].nxt=head[u];
        head[u]=cnt;
    }
    
    inline int Get(int x,int y){
        return (x-1)*(m+1)+y;
    }
    
    inline void add1(int x,int y,int w){// 右上->左下
        add(Get(x,y+1),Get(x+1,y),w);
        add(Get(x+1,y),Get(x,y+1),w);
    }
    
    inline void add2(int x,int y,int w){// 左上->右下
        add(Get(x,y),Get(x+1,y+1),w);
        add(Get(x+1,y+1),Get(x,y),w);
    }
    
    inline void Init(int w){
        for(int i=1;i<=n;i++){
            scanf("%s",s+1);
            for(int j=1;j<=m;j++){
                if(s[j]!='.'){
                    if(s[j]=='/')add1(i,j,w);
                    else if(s[j]=='X'){
                        add1(i,j,w);add2(i,j,w);        
                    }else add2(i,j,w);
                }
            }
        }
    }
    
    int res;
    bool vis[maxn];
    void dfs(int u){
        vis[u]=1;
        res+=abs(face[u]-rear[u]);
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].to;
            if(vis[v])continue;
            dfs(v);
        }
    }
    
    int main(){
    #ifndef LOCAL
        freopen("stitch.in","r",stdin);
        freopen("stitch.out","w",stdout);
    #endif
        scanf("%d%d",&n,&m);
        Init(1);Init(-1);
        for(int i=1;i<=n+1;i++)
            for(int j=1;j<=m+1;j++){
                int now=Get(i,j);
                if(!f[now]||vis[now])continue;
                res=0;
                dfs(now);
                ans+=res?(res>>1):1;
            }
        printf("%d
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    CSS简单的四种引入方式
    html之表单标签
    html基础标签之head和body标签
    Python之协程的实现
    Python之实现不同版本线程池
    Python多进程之multiprocessing模块和进程池的实现
    Python之queue模块
    sqlserver 时间格式化
    关于sql server 代理(已禁用代理xp)解决办法
    sqlserver如何启动数据库邮件
  • 原文地址:https://www.cnblogs.com/Midoria7/p/13794644.html
Copyright © 2011-2022 走看看