• $Codeforces$ $Round$ $620$ $Div.2$ 简要题解


    从鸽王那儿看到的一组好题, (Itst) 推荐的,作为复健第一套题试试水。自己做了 (A)(B)(E) 三个,道阻且长。

    (A-Two) (Rabbits)

    解法

    结论显然, ((a+b)|(y-x)) 时在整数秒相遇,相遇所用时间为 (frac{y-x}{a+b}) 秒;否则不能相遇。

    (Code)

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    using namespace std;
    inline int read()
    {
    	int ret=0,f=1;
    	char ch=getchar();
    	while(ch>'9'||ch<'0')
    	{
    		if(ch=='-')
    			f=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9')
    	{
    		ret=(ret<<1)+(ret<<3)+ch-'0';
    		ch=getchar();
    	}
    	return ret*f;
    }
    int t,x,y,a,b;
    int main()
    {
    	t=read();
    	for(register int i=1;i<=t;i++)
    	{
    		x=read();
    		y=read();
    		a=read();
    		b=read();
    		y=y-x;
    		b=a+b;
    		if(y%b==0)
    			printf("%d
    ",y/b);
    		else
    			printf("-1
    ");
    	}
    	return 0;
    }
    

    (B-) (Longest) (Palindrome)

    解法

    注意到 (n leqslant 100)(m leqslant 50) ,故考虑 (O(n^2m)) 逐字扫描任意两个串是否互为回文串,标记下来分左右两边输出,另外判断是否有本身就是回文串的字符串放在正中间即可。

    (Code)

    #include<bits/stdc++.h>
    using namespace std;
    inline int read()
    {
    	int ret=0,f=1;
    	char ch=getchar();
    	while(ch>'9'||ch<'0')
    	{
    		if(ch=='-')
    			f=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9')
    	{
    		ret=(ret<<1)+(ret<<3)+ch-'0';
    		ch=getchar();
    	}
    	return ret*f;
    }
    int n,m,cnt=0,slfnum=0,l[105],r[105];
    char str[105][55];
    bool flag,slf;
    int main()
    {
    	n=read();
    	m=read();
    	for(register int i=1;i<=n;i++)
    		scanf("%s",str[i]);
    	for(register int i=1;i<=n;i++)
    	{
    		slf=1;
    		for(register int j=0;j<m;j++)
    			if(str[i][j]!=str[i][m-1-j])
    				slf=0;
    		if(slf)
    			slfnum=i;
    		for(register int j=i+1;j<=n;j++)
    		{
    			flag=1;
    			for(register int k=0;k<m;k++)
    				if(str[i][k]!=str[j][m-1-k])
    					flag=0;
    			if(flag)
    			{
    				cnt++;
    				l[cnt]=i;
    				r[cnt]=j;
    			}
    		}
    	}
    	if(slfnum==0)
    		printf("%d
    ",2*m*cnt);
    	else
    		printf("%d
    ",2*m*cnt+m);
    	for(register int i=1;i<=cnt;i++)
    		printf("%s",str[l[i]]);
    	if(slfnum>0)
    		printf("%s",str[slfnum]);
    	for(register int i=cnt;i>=1;i--)
    		printf("%s",str[r[i]]);
    	printf("
    ");
    	return 0;
    }
    

    (C-) (Air) (Conditioner) (好题)

    解法

    首先提出引理:任一时刻可能达到的温度是一段连续(整数)区间。由于每一分钟到下一分钟的温度变化只能为 (+1)(-1)(0) ,故引理正确。由引理,将问题转化为任一时刻求出的可能区间是否满足顾客的需求,问题的实质是一个区间比较问题,时间复杂度 (O(q sum n)) 。具体求出可能的温度区间的方式是标记上下界,并随时间变化扩大上下界,随顾客需求变化缩小上下界。

    提示

    本题能做到线性的关键是不考虑具体的路径,只考虑路径的集合(以可能的温度区间呈现)。换言之,具体的合法路径是什么对于本题来说并不重要。

    思考:上面做法的正确性?

    个人认为在于最终所选路径只有合法和不合法两种,该做法筛去了所有不合法的路径(疑问:怎么证明?),只留下了保证是合法路径的路径集合。欢迎在评论区证明该题做法的正确性。

    (Code)

    #include<bits/stdc++.h>
    using namespace std;
    inline int read()
    {
    	int ret=0,f=1;
    	char ch=getchar();
    	while(ch>'9'||ch<'0')
    	{
    		if(ch=='-')
    			f=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9')
    	{
    		ret=(ret<<1)+(ret<<3)+ch-'0';
    		ch=getchar();
    	}
    	return ret*f;
    }
    int q,n,m,lm,rm,t[105],l[105],r[105];
    bool flg;
    int main()
    {
    	q=read();
    	for(register int i=1;i<=q;i++)
    	{
    		n=read();
    		m=read();
    		t[0]=0;
    		flg=1;
    		for(register int j=1;j<=n;j++)
    		{
    			t[j]=read();
    			l[j]=read();
    			r[j]=read();
    		}
    		lm=m;
    		rm=m;
    		for(register int j=1;j<=n;j++)
    		{
    			lm=lm-(t[j]-t[j-1]);
    			rm=rm+(t[j]-t[j-1]);
    			if(r[j]<lm||rm<l[j])
    				flg=0;
    			lm=max(lm,l[j]);
    			rm=min(rm,r[j]);
    		}
    		if(lm>rm)
    			flg=0;
    		if(flg)
    			printf("YES
    ");
    		else
    			printf("NO
    ");
    	}
    	return 0;
    }
    

    (D-) (Shortest) (and) (Longest) (LIS) (好题)

    解法

    考虑怎样构造这个序列。(该题的考点是构造)下面分别讲解错误的和正确的思路。
    错解:首先考虑怎样满足大小限制,再通过安排数的(相对)大小实现 (LIS) 最长/最短。
    从错解走向正解:怎样安排数的(相对)大小呢?显然是使满足大小关系的小数放左边、大数放右边时 (LIS) 最长,最短的情况同理。
    正解:反向考虑,首先不安排大小顺序,先考虑无大小限制时怎样使 (LIS) 最长/最短。显然,递增序列 (lbrace 1,2,3,...,n brace)(LIS) 最长,递减序列 (lbrace n,n-1,n-2,...,1 brace)(LIS) 最短。接下来考虑大小关系限制,对于构造好的序列,将连续 (">")(LIS) 最长序列的子序列反转、将连续 ("<")(LIS) 最短序列的子序列反转即可满足大小限制。

    补充

    (LIS) 长度的下界是连续 ("<") 的长度。达到该长度的构造如上。 (LIS) 长度的上界是连续 (">") 区间的个数加上连续 (">") 区间以外数的个数(另一种答案是 ("<") 的个数 (+1) )。达到该长度的构造如上。

    (Code)

    #include<bits/stdc++.h>
    using namespace std;
    inline int read()
    {
    	int ret=0,f=1;
    	char ch=getchar();
    	while(ch>'9'||ch<'0')
    	{
    		if(ch=='-')
    			f=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9')
    	{
    		ret=(ret<<1)+(ret<<3)+ch-'0';
    		ch=getchar();
    	}
    	return ret*f;
    }
    int t,n,l,lismin[200005],lismax[200005];
    char str[200005];
    bool flag,slf;
    int main()
    {
    	t=read();
    	for(register int i=1;i<=t;i++)
    	{
    		n=read();
    		scanf("%s",str);
    		for(register int j=1;j<=n;j++)
    		{
    			lismax[j]=j;
    			lismin[j]=n+1-j;
    		}
    		l=0;
    		for(register int j=0;j<n-1;j++)
    		{
    			if(str[j]=='<')
    			{
    				if(l==0)
    					l=j+1;
    			}
    			else
    			{
    				if(l>0)
    				{
    					reverse(lismin+l,lismin+j+2);
    					l=0;
    				}
    			}
    		}
    		if(l>0)
    		{
    			reverse(lismin+l,lismin+n+1);
    			l=0;
    		}
    		for(register int j=0;j<n-1;j++)
    		{
    			if(str[j]=='>')
    			{
    				if(l==0)
    					l=j+1;
    			}
    			else
    			{
    				if(l>0)
    				{
    					reverse(lismax+l,lismax+j+2);
    					l=0;
    				}
    			}
    		}
    		if(l>0)
    		{
    			reverse(lismax+l,lismax+n+1);
    			l=0;
    		}
    		for(register int j=1;j<n;j++)
    			printf("%d ",lismin[j]);
    		printf("%d
    ",lismin[n]);
    		for(register int j=1;j<n;j++)
    			printf("%d ",lismax[j]);
    		printf("%d
    ",lismax[n]);
    	}
    	return 0;
    }
    

    (E) (1-Trees) (and) (Queries)

    解法

    思路简单,细节比较多。考虑加边 ((x,y)) 之后形成一个环,任意两点 (a,b) 之间的最短路径有三种可能: (a-x-y-b)(a-y-x-b)(a-b) 。由于同一条边、同一个点可以重复经过,这三种路径长度任意一个不比 (k) 大且与 (k) 的奇偶性相同即可。(这是一个简化的做法,因为可以无限前进偶数步,所以实际上只需考虑奇偶性)

    细节

    注意树的建图是双向的。注意倍增 (LCA) 的一些细节。

    (Code)

    #include<bits/stdc++.h>
    using namespace std;
    inline int read()
    {
    	int ret=0,f=1;
    	char ch=getchar();
    	while(ch>'9'||ch<'0')
    	{
    		if(ch=='-')
    			f=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9')
    	{
    		ret=(ret<<1)+(ret<<3)+ch-'0';
    		ch=getchar();
    	}
    	return ret*f;
    }
    int n,u,v,queries,x,y,a,b,k,dis1,dis2,dis3,rlen,r,edgenum,t;
    int ver[200005],w[200005],nxt[200005],head[100005],d[100005],dist[100005],f[100005][18];
    queue<int> q;
    inline void addedge(int x,int y,int z)
    {
    	ver[++edgenum]=y;
    	w[edgenum]=z;
    	nxt[edgenum]=head[x];
    	head[x]=edgenum;
    }
    inline void bfs()
    {
    	q.push(1);
    	d[1]=1;
    	while(q.size())
    	{
    		int x=q.front();
    		q.pop();
    		for(register int i=head[x];i;i=nxt[i])
    		{
    			int y=ver[i];
    			if(d[y])
    				continue;
    			d[y]=d[x]+1;
    			dist[y]=dist[x]+w[i];
    			f[y][0]=x;
    			for(register int j=1;j<=t;j++)
    				f[y][j]=f[f[y][j-1]][j-1];
    			q.push(y);
    		}
    	}
    }
    inline int lca(int x,int y)
    {
    	if(d[x]>d[y])
    		swap(x,y);
    	for(register int i=t;i>=0;i--)
    		if(d[f[y][i]]>=d[x])
    			y=f[y][i];
    	if(x==y)
    		return x;
    	for(register int i=t;i>=0;i--)
    	{
    		if(f[y][i]!=f[x][i])
    		{
    			y=f[y][i];
    			x=f[x][i];
    		}
    	}
    	return f[x][0];
    }
    int main()
    {
    	n=read();
    	for(register int i=1;i<n;i++)
    	{
    		u=read();
    		v=read();
    		addedge(u,v,1);
    		addedge(v,u,1);
    	}
    	t=(int)(log(n)/log(2))+1;
    	bfs();
    	queries=read();
    	for(register int i=1;i<=queries;i++)
    	{
    		x=read();
    		y=read();
    		a=read();
    		b=read();
    		k=read();
    		dis1=dist[a]+dist[b]-2*dist[lca(a,b)];
    		dis2=dist[a]+dist[x]-2*dist[lca(a,x)]+dist[y]+dist[b]-2*dist[lca(y,b)]+1;
    		dis3=dist[b]+dist[x]-2*dist[lca(b,x)]+dist[y]+dist[a]-2*dist[lca(y,a)]+1;
    		rlen=dist[x]+dist[y]-2*dist[lca(x,y)]+1;
    		if((k>=dis1)&&((k-dis1)%2==0))
    		{
    			printf("YES
    ");
    			continue;
    		}
    		if((k>=dis2)&&((k-dis2)%2==0))
    		{
    			printf("YES
    ");
    			continue;
    		}
    		if((k>=dis3)&&((k-dis3)%2==0))
    		{
    			printf("YES
    ");
    			continue;
    		}
    		printf("NO
    ");
    	}
    	return 0;
    }
    

    未完待续

  • 相关阅读:
    《梦幻西游》打响反盗号战役:为2亿玩家提供360安全武器 狼人:
    瑞星发表官方声明:记者王学武系恶意诽谤 狼人:
    中美联合挫败Conficker蠕虫大攻击 狼人:
    安全机构建议奥巴马政府谨慎使用开源软件 狼人:
    大量计算机遭“灰鸽子”病毒攻击 狼人:
    警惕IE7新漏洞导致的木马病毒暴增 狼人:
    IE7漏洞被瞄准 新型攻击将爆发 狼人:
    Windows 7成为Pwn2own黑客挑战赛目标 狼人:
    黑客入侵唱片业协会网站 为“海盗湾”助威 狼人:
    “猫癣”“犇牛”木马病毒肆虐 专家提醒补漏洞 狼人:
  • 原文地址:https://www.cnblogs.com/Peter0701/p/15144245.html
Copyright © 2020-2023  润新知