1.【LGR-150-Div.2】洛谷 8 月月赛 I & RiOI Round 2

【LGR-150-Div.2】洛谷 8 月月赛 I & RiOI Round 2

T1 P9496 「RiOI-2」hacker

\(100pts\)

题目描述

有两种操作,\(「ACCEPT」\)\(「BOTH」\),均花费 \(1\) 代价。\(「ACCEPT」\)\(n\) 二进制 按位或 一个正整数。\(「BOTH」\)\(n\) 二进制 按位与 一个正整数。两种操作均可使用多次(或不用),请求出将 \(n\) 变为 \(m\) 最小的代价。

帮助:什么是按位与和按位或

  • 水题,但是一开始用 \(scanf\) 输入 \(long long\) 没有用 "%lld" 然后 \(WA\) 了两个点。
    • \(n\)\(m\) 转化为二进制,用两个数组分别存起来,再枚举。
    • \(n\) 表示为 \(0\)\(b\) 表示为1,则进行\(「ACCEPT」\)操作。
    • \(m\) 表示为 \(1\)\(b\) 表示为0,则进行\(「BOTH」\)操作。
#include<bits/stdc++.h>
using namespace std;
long long int n,sum=0;
long long int x,y;
long long int a[100],b[100];
int main()
{
	long long int i,j,m,t;
	scanf("%lld",&t);
	while(t--)
	{
		sum=0;
		scanf("%lld%lld",&n,&m);//scanf输入long long需要 "%lld"
		if(n==m)
		{
			printf("%d\n",0);
			continue;
		}
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));
		int cnt=0,res=0;
		int cn=0,cm=0;
		x=n,y=m;
		while(x)
		{
			a[++cnt]=x%2;
			x/=2;
		}
		while(y)
		{
			b[++res]=y%2;
			y/=2;
		}
		for(i=1;i<=max(cnt,res);i++)
		{
			if(a[i]==1&&b[i]==0)cn=1;
			if(a[i]==0&&b[i]==1)cm=1;
		}
		printf("%d\n",cn+cm);
	}
}
  • 听机房大佬说,其实有一个 \(O(1)\) 算法,如果 \(m==n\),就输出\(0\),如果 \((m|n)==m\) 或者 \((m|n)==n\) 输出\(1\),如果都不符合,输出 \(2\)
  • \(n\) 表示为 \(100010\)\(m\) 表示为 \(110010\),此时使 \(n\) 按位或 \(m\),结果为\(110010\),等于\(m\)
  • \(n\) 表示为 \(10010\)\(m\) 表示为 \(110010\),此时使 \(n\) 按位或 \(m\),结果为\(110010\),等于\(m\)
  • \(n\) 表示为 \(10110010\)\(m\) 表示为 \(110010\),此时使 \(n\) 按位或 \(m\),结果为\(10110010\)。等于\(n\)
  • \(n\) 表示为 \(110110\)\(m\) 表示为 \(110010\),此时使 \(n\) 按位或 \(m\),结果为\(110110\),等于\(n\)
  • \(n\) 表示为 \(110101\)\(m\) 表示为 \(110010\) 时,此时使 \(n\) 按位或 \(m\),结果为\(110111\),不等于 \(n\),也不等于 \(m\),此时需要用两种操作。
  • 即当按位或后,若 \(m\)\(n\) 有不同的位,如有一位 \(n\)\(0\)\(m\)\(1\)。并且如果有一位 \(n\)\(1\)\(m\)\(0\),也就是 \((m|n)\) 不等于 \(m\) 也不等于 \(n\)。则需要进行两次操作。
#include<bits/stdc++.h>
int main()
{
	long long int t,n,m;
	scanf("%ld",&t);
	while(t--)
	{
		scanf("%ld%ld",&n,&m);
		if(n==m) printf("0\n");
		else if((n|m)==m||(n|m)==n) printf("1\n");
		//位运算优先级较低,需要用括号括起来。
		else printf("2\n");
	}
}

T2 P9497 「RiOI-2」weight

\(100pts\)

题目描述

给定一个 \(n\)\(n\) 列 的矩阵 \(a\)

\(q\) 组询问,每次给定一个 \(v\),请将矩阵每一行任意重排(可以不重排),最大化最大值不小于 \(v\)(也就是说,至少有一个不小于 \(v\) 的数)的列数。请输出这个列数。

询问之间相互独立。换言之,每次询问前可以重新排列。

  • 还是水题,将二维矩阵压成一位数组,输入后再从大到小 \(sort\) 一遍,每次询问时,从 \(1\)\(n\) 中寻找第一个小于 \(v\) 的数,如果都大于等于 \(v\) 则,输出 \(n\)
    • 一开始没看懂题目,以为是把每行从大到小排列。没看到最大化每排的最大值。
    • 改过来之后,发现输出都是 \(0\) ,但没想太多就交上去了,但是竟然 \(AC\) 了,才想起来电脑是\(32\)位,\(long long\) 显示为\(0\)
#include<bits/stdc++.h>
using namespace std;
long long int n,q,a[1010001];
long long int maxx[1001];
bool cmp(long long int x,long long int y)
{
	return x>y;
}
int main()
{
	int i,j;
	cin>>n>>q;
	for(i=1;i<=n;i++)
		for(j=1;j<=n;j++)
			scanf("%ld",&a[(i-1)*n+j]);
	stable_sort(a+1,a+1+n*n,cmp);
	for(i=1;i<=n;i++)maxx[i]=a[i];
	long long int w;
	for(i=1;i<=q;i++)
	{
		int flag=0;
		scanf("%ld",&w);
		for(j=1;j<=n;j++)
		{
			if(maxx[j]<w)
			{
				printf("%ld\n",j-1);
				flag=1;
				break;
			}
		}
		if(!flag)printf("%ld\n",n);
	}
}

T3 P9498 「RiOI-2」equals

\(100pts\)

题目描述

给定一棵 \(n\) 个结点,以 \(1\) 为根的树,定义一个结点的深度 \(d_i\) 表示它到根结点的简单路径上的结点个数。

你需要给每个结点黑白染色,满足黑色结点的深度和等于白色结点的深度和。设 \(c_i = \{0, 1\}\) 分别代表编号为 \(i\) 的结点为黑色或白色,那么这即 \(\displaystyle\sum_{c_i=0}d_i=\sum_{c_i=1}d_i\)

若无解,仅输出一行一个整数 \(-1\)

  • 一开始打算用邻接矩阵存边,但是...

数据规模与约定

\(\rm Subtask\) 分值 $n\le $ 特殊性质
\(0\) \(5\) \(20\) /
\(1\) \(15\) \(500\) /
\(2\) \(20\) \(5\times 10^3\) /
\(3\) \(10\) / \(n\) 为偶数
\(4\) \(5\) / 树为菊花图(不保证根为菊花中心)
\(5\) \(5\) / 树为一条链(不保证根为链的端点)
\(6\) \(40\) / /

斜杠表示这一栏无特殊限制。

对于 \(100\%\) 的数据,\(1\le n\le 10^6\)\(1\le u_i,v_i\le n\),输入数据构成一棵树。

  • 由于\(1\le n\le 10^6\),所以改用链式前向星存边。
  • \(dfs\) 一遍得出深度,由于黑点、白点的深度和要相等,所以想到前几天做的分钱,于是用\(dp\)判断是否有解。
  • 但是如果有解需要输出点的状态,而本蒟蒻不会记录路径,因此放弃了\(dp\)
  • 放弃了\(dp\),考虑爆搜,打搜索。因为爆搜超时,开始一分没骗到,然后剪枝得了\(85\)
    之后想到折半搜索,但是本蒟蒻只会折半,因此分了两个\(for\)循环,第一个从\(1\)循环到\((n+1)/2\)
    第二个从\((n+1)/2\)循环到\(n\)。(但应该不是正解)。然后\(AC\)了......

(出题人的数据怎么如此之氵,把爆搜都放过去了)...

测评记录

#include<bits/stdc++.h>
using namespace std;
long long int dep[6000001],vis[6000001];
long long int n,sum=0;
struct ee
{
	int next,to;
}e[6000001];
int head[6000001],cnt=0;
void add(int u,int v)
{
	e[++cnt]={head[u],v};
	head[u]=cnt;
}
void dfs(int x)
{
	int i;
	vis[x]=1;
	for(i=head[x];i;i=e[i].next)
	{
		int v=e[i].to;
		if(!vis[v])
			dep[v]=dep[x]+1,dfs(v);
	}
}
bool pd[6000001];
void search(int x,long long int s)
{
	int i,j;
	if(s==(sum/2))
	{
		for(j=1;j<=n;j++)
		{
			if(pd[j])printf("%d ",1);
			else printf("%d ",0);
		}
		exit(0);
	}
	for(i=x;i<=(n+1)/2;++i)
	{
		if(!pd[i]&&s+dep[i]<=(sum/2))
		{
			pd[i]=1;
			if(s+dep[i]==(sum/2))
			{
				for(j=1;j<=n;j++)
				{
					if(pd[j])printf("%d ",1);
					else printf("%d ",0);
				}
				exit(0);
			}
			if(s+dep[i]<(sum/2))
				search(i+1,s+dep[i]);
			pd[i]=0;
		}
	}
	for(i=(n+1)/2;i<=n;++i)
	{
		if(!pd[i]&&s+dep[i]<=(sum/2))
		{
			pd[i]=1;
			if(s+dep[i]==(sum/2))
			{
				for(j=1;j<=n;j++)
				{
					if(pd[j])printf("%d ",1);
					else printf("%d ",0);
				}
				exit(0);
			}
			if(s+dep[i]<(sum/2))
				search(i+1,s+dep[i]);
			pd[i]=0;
		}
	}
}
int main()
{
	int i,j,m,x,y;
	scanf("%d",&n);
	for(i=1;i<n;i++)scanf("%d%d",&x,&y),add(x,y),add(y,x);
	dep[1]=1;
	dfs(1);
	for(i=1;i<=n;++i)sum+=dep[i];
	if(sum%2==1)
	{
		printf("%d",-1);
		return 0;
	}
	search(1,0);
	printf("%d",-1);
}

T4 P9499 「RiOI-2」change

\(20pts\)

  • 本蒟蒻不会打,骗了20分。。。
posted @ 2023-08-05 21:11  minecraft114514  阅读(100)  评论(4编辑  收藏  举报