zoukankan      html  css  js  c++  java
  • atcoder ZONe contest 题解

    Sneaking

    题目描述

    点此看题

    解法

    不难看出是最短路,一开始人傻了,直接暴力建图卡了好久,但是最后草过去了

    复杂度瓶颈在于四类边,发现就是多了 (1) 的花费有点难搞。可以采用建虚点的思想,我们按 (F) 进入坦克模式,花费为 (1),坦克模式开到上一个点的坦克模式花费也为 (1),这样边数就是 (O(nm)) 的了。

    让你们欣赏一下我暴力是怎么草过去的

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <queue>
    using namespace std;
    const int M = 300005;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,tot,f[M],dis[M];
    struct edge
    {
        int v,c,next;
    }e[10*M];
    struct node
    {
        int u,c;
        bool operator < (const node &R) const
        {
            return c>R.c;
        }
    };
    int id(int x,int y)
    {
        return (x-1)*m+y;
    }
    void add(int u,int v,int c)
    {
        e[++tot]=edge{v,c,f[u]},f[u]=tot;
    }
    void dijk()
    {
        priority_queue<node> q;
        memset(dis,0x3f,sizeof dis);
        q.push(node{1,0});dis[1]=0;
        while(!q.empty())
        {
            int u=q.top().u,t=q.top().c;q.pop();
            if(t>dis[u]) continue;
            for(int i=f[u];i;i=e[i].next)
            {
                int v=e[i].v,c=e[i].c;
                if(dis[v]>dis[u]+c)
                {
                    dis[v]=dis[u]+c;
                    q.push(node{v,dis[v]});
                }
            }
            int x=(u-1)/m+1,y=(u-1)%m+1;
            for(int i=1;i<x;i++)
            {
                int v=id(x-i,y),c=i+1;
                if(dis[v]>dis[u]+c)
                {
                    dis[v]=dis[u]+c;
                    q.push(node{v,dis[v]});
                }
            }
        }
    }
    signed main()
    {
        n=read();m=read();
        for(int i=1;i<=n;i++)
            for(int j=1;j<m;j++)
            {
                int x=read();
                add(id(i,j),id(i,j+1),x);
                add(id(i,j+1),id(i,j),x);
            }
        for(int i=1;i<n;i++)
            for(int j=1;j<=m;j++)
            {
                int x=read();
                add(id(i,j),id(i+1,j),x);
            }
        dijk();
        printf("%d
    ",dis[id(n,m)]);
    }
    
    

    Encounter and Farewell

    题目描述

    ([0,2^n)) 这些点,给定一个长度为 (m) 的数组 (a),如果两个点之间的异或值 ( ot= a_i),就可以将这两个点连边,尝试构造出一棵合法的生成树,如果不存在这样的生成树输出-1

    (1leq nleq 18)

    解法

    我一开始想直接构造生成树发现做不动,正解需要考虑生成树存在的条件。

    如果原图任意两点之间都可以到达,那说明存在生成树。尝试翻译这个条件,就相当于存在一条路径可以互相到达,设数组 (a) 关于全集的补集是 (b),那么就相当于异或若干次 (b) 中的元素可以得到 ([0,2^n)) 的所有元素。

    那么不就是看 (b) 的线性基是否满秩吗?

    判断有无解之后怎么构造最终的答案呢?线性基里的数是若干个原数异或的结果,那么对于每个点我们只用考虑线性基里原数的边即可,暴力枚举每条边,时间复杂度 (O(nlog n))

    #include <cstdio>
    const int M = 1000005;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,k,cnt,a[M],b[M],fa[M],fb[M];
    int find(int x)
    {
    	if(x!=fa[x]) return fa[x]=find(fa[x]);
    	return fa[x];
    }
    signed main()
    {
    	n=read();m=read();
    	while((2<<k)<n) k++;
    	for(int i=1;i<=m;i++)
    		fb[read()]=1;
    	for(int i=1;i<n;i++)
    	{
    		if(fb[i]) continue;
    		int x=i;
    		for(int j=k;j>=0 && x;j--)
    		{
    			if((x&(1<<j)) && !a[j])
    			{
    				a[j]=x;b[j]=i;cnt++;
    				break;
    			}
    			if(x&(1<<j)) x^=a[j];
    		}
    	}
    	for(int i=0;i<n;i++) fa[i]=i;
    	if(cnt<=k) {puts("-1");return 0;}
    	for(int i=0;i<n;i++)
    		for(int j=0;j<=k;j++)
    			if(find(i)!=find(i^b[j]))
    			{
    				printf("%d %d
    ",i,i^b[j]);
    				fa[find(i)]=find(i^b[j]);
    			}
    }
    
  • 相关阅读:
    技术的极限(8): 集成与分离
    心智与认知(1): 反馈循环(Feedback loop)
    证明与计算(6): 身份认证与授权
    证明与计算(5): 从加密哈希函数到一致性哈希
    技术的极限(6): 密码朋克精神(Cypherpunk Spirit)
    翻译(3): NULL-计算机科学上最糟糕的失误
    工具(5): 极简开发文档编写(How-to)
    证明与计算(3): 二分决策图(Binary Decision Diagram, BDD)
    证明与计算(2): 离散对数问题(Discrete logarithm Problem, DLP)
    翻译(2): How to Write a 21st Century Proof
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/14725551.html
Copyright © 2011-2022 走看看