• Atcoder Grand Contest 025


    025E Walking on a Tree

    题目描述

    点此看题

    解法

    \(c_i\) 表示边 \(i\) 被路径覆盖的次数,考虑答案的上界是 \(\sum\min(2,c_i)\)

    从叶子开始构造,考虑叶子 \(u\) 和它的父亲 \(v\):如果 \(c_{(u,v)}=0\),那么不需要覆盖;如果 \(c_{(u,v)}=1\),那么覆盖方向是没有关系的;下面考虑 \(c_{(u,v)}\geq 2\) 的情况。

    任取两条路径 \([u,a],[u,b]\)(需要先调整成左端点是 \(u\)),那么使得 \(u\) 父边带来的限制就是两条路径方向相反,然后我们可以把这两条路径等效成 \([a,b]\),考虑那些没有被正反覆盖的边,它们的 \(c\) 都不会边,而被正反覆盖的边它们的 \(c\) 虽然变了但也无关紧要,所以可以归纳下去。

    直接模拟删叶子即可,时间复杂度 \(O(nm)\)

    总结

    构造答案,常见的思路是使用归纳法,往往需要等效法的结合,使用等效法是注意要先观察限制,然后思考解决这个限制需要问题中哪些结构的变化,这些变化能不能等效到子问题?

    #include <cstdio>
    #include <bitset>
    #include <vector>
    #include <iostream>
    using namespace std;
    const int M = 2005;
    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,fa[M],a[M],b[M],c[M],d[M],x[M],y[M],rev[M],f[M];
    vector<int> g[M];bitset<M> z[M];int ans,tp,s[M],dep[M];
    void dfs(int u,int p)
    {
    	dep[u]=dep[p]+1;fa[u]=p;
    	for(int v:g[u]) if(v^p) dfs(v,u);
    }
    void walk(int u,int v,int id)
    {
    	while(u!=v)
    	{
    		if(dep[u]>dep[v]) z[id][u]=1,c[u]++,u=fa[u];
    		else z[id][v]=1,c[v]++,v=fa[v];
    	}
    }
    void del(int o)
    {
    	for(int i=1,lst=0,fl=0;i<=m;i++) if(z[i][o])
    	{
    		if(y[i]==o) rev[i]^=1,swap(x[i],y[i]);
    		z[i][o]=0;x[i]=fa[o];
    		if(!lst || fl) {lst=i;continue;}
    		z[lst]=z[i]^z[lst];rev[i]^=rev[lst]^1;
    		z[i].reset();f[i]=lst;x[lst]=y[i];lst=0;fl=1;
    	}
    }
    void get(int u)
    {
    	rev[u]^=rev[f[u]];
    	for(int i=1;i<=m;i++)
    		if(f[i]==u) get(i);
    }
    signed main()
    {
    	n=read();m=read();
    	for(int i=1;i<n;i++)
    	{
    		int u=read(),v=read();
    		g[u].push_back(v);
    		g[v].push_back(u);
    		d[u]++;d[v]++;
    	}
    	dfs(1,0);
    	for(int i=1;i<=m;i++)
    	{
    		a[i]=x[i]=read();
    		b[i]=y[i]=read();
    		walk(x[i],y[i],i);
    	}
    	for(int i=2;i<=n;i++)
    		if(d[i]==1) s[++tp]=i;
    	for(int i=1;i<n;i++)
    	{
    		int u=s[tp--],v=fa[u];
    		del(u);
    		if((--d[v])==1 && v!=1) s[++tp]=v;
    	}
    	for(int i=2;i<=n;i++) ans+=min(2,c[i]);
    	for(int i=1;i<=m;i++) if(!f[i]) get(i);
    	printf("%d\n",ans);
    	for(int i=1;i<=m;i++)
    	{
    		if(rev[i]) swap(a[i],b[i]);
    		printf("%d %d\n",a[i],b[i]);
    	}
    }
    

    025F Addition and Andition

    题目描述

    点此看题

    解法

    考虑拆分法,我们考虑初始的每对 \((1,1)\),如果不考虑其它数字的情况下,它们是会一直向前移动的。如果 \((1,1)\) 的前面有 \((1,0)/(0,1)\) 之类的数对,就会阻拦 \((1,1)\) 的移动,例子:

    start -> 01001   01010   01100  crash -> 10000  end
             00001   00010   00100           01000
    

    考虑从高位到低位来处理 \((1,1)\) 的移动,我们可以暴力把它往前移动至第一个有 \(1\) 的位置,然后考虑进位,如果进位之后还是 \((1,1)\) 可以继续移动,否则移动停止。

    那么暴力做时间复杂度是否合理呢?我们每次 \((1,1)\) 和第一个有 \(1\) 的位置碰撞,都会造成至少一个 \(1\) 的减少,而 \(1\) 的个数永不增加,所以我们用栈维护第一个有 \(1\) 的位置,时间复杂度 \(O(n)\)

    #include <cstdio>
    #include <iostream>
    using namespace std; 
    const int M = 2000005;
    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,tp,x[M],y[M],a[M],tmp[M];char s[M],t[M];
    signed main()
    {
    	n=read();m=read();k=read();
    	scanf("%s%s",s+1,t+1);
    	for(int i=1;i<=n;i++) x[i]=s[n-i+1]-'0';
    	for(int i=1;i<=m;i++) y[i]=t[m-i+1]-'0';
    	a[++tp]=max(n,m)+k+1;
    	for(int i=max(n,m);i>=1;i--)
    	{
    		int cnt=0;
    		for(int z=k,j=i;;)
    		{
    			while(tp && a[tp]<=j) tp--;
    			int nxt=a[tp];tmp[++cnt]=j;
    			if(x[j]==1 && y[j]==1 && z)//shift
    			{
    				x[j]=y[j]=0;
    				if(z>=nxt-j) z-=nxt-j;
    				else nxt=j+z,z=0;
    				x[nxt]++;y[nxt]++;j=nxt;
    			}
    			else if(x[j]==2 || y[j]==2)//carry bit
    			{
    				x[j+1]+=x[j]>>1;x[j]&=1;
    				y[j+1]+=y[j]>>1;y[j]&=1;j++;
    			}
    			else break;//end
    		}
    		for(int j=cnt;j>=1;j--)
    			if(x[tmp[j]] || y[tmp[j]]) a[++tp]=tmp[j];
    	}
    	for(n+=k;!x[n];n--);for(m+=k;!y[m];m--);
    	for(int i=n;i>=1;i--) printf("%d",x[i]);
    	puts("");
    	for(int i=m;i>=1;i--) printf("%d",y[i]);
    	puts("");
    }
    
  • 相关阅读:
    英国黑客试图以10万美元勒索苹果
    暗网现大量智能门锁密码,物联网黑客攻击已成常态
    黑客安全专家郭盛华:逃避僵尸网络恶意软件攻击的13种方法
    前端好网站汇总
    中国标准时间转换
    前端各种安装总结
    vue项目如何调用高德地图
    js访问jsion数据动态显示在html页面
    Excel删除重复数据及用公式筛选重复项并标记颜色突出显示
    excel的公式中同一行的一个单元格只能被引用一次
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/16032635.html
Copyright © 2020-2023  润新知