P6691 选择题

题目背景

小 L 喜欢逻辑推理。

一天,他在一本由英国哲士沃·协德编写的《我也不知道为什么要叫这个名字的一本有关逻辑学的书》中翻到了一道奇特的问题,但他并不会做。他知道你善于用程序解决问题,于是决定让你来帮助他完成这些问题。

题目描述

这是一道有 n 个选项的选择题,每个选项的内容都很独特。第 i 个选项的内容的形式如下:

  • 第 ai​ 个选项是正确/错误的

小 L 认为这种题目的答案不一定是唯一的,所以他想问题这道题有多少种合法的答案(可以全部正确或全部错误)。他还想问你这么多答案中,正确选项最多和最少的答案分别有多少个正确选项。

当然,如果这道题不存在合法的答案,你可以直接回答小 L No answer

输入格式

第一行有一个正整数 n,表示选项个数。

接下来 n 行,每行有两个整数 ai​,opti​,描述一个选项。其中当 opti​=1 时,表示这个选项的内容为 第 ai​ 个选项是正确的;当 opti​=0 时,表示这个选项的内容为 第ai​ 个选项是错误的

输出格式

如果没有答案满足这道选择题,输出No answer

否则输出三行,每行一个正整数,分别为合法答案数及正确选项最多和最少的答案分别有多少个正确选项。其中合法答案数要对 998244353 取模。

输入输出样例

输入 #1

4
2 1
4 0
1 1
2 0

输出 #1

2
3
1

输入 #2

10
4 1
7 0
2 0
3 1
7 1
5 0
9 1
10 1
8 0
1 1

输出 #2

No answer

说明/提示

对于样例一,一共有下面 2 种正确答案:

  • 第 1,2,3 个选项是正确的。
  • 第 4 个选项是正确的。

其中正确选项最多的答案有 33 个选项正确,正确选项最少的答案有 11 个选项正确。

数据范围

对于 10% 的数据,n≤10。
对于 30%的数据,n≤100。
对于 60%的数据,n≤103。
对于 100% 的数据,n≤106,1≤ai​≤n,i≠ai​,opti​∈{0,1}。

#include <bits/stdc++.h>
using namespace std;
int f[1000005],r[1000005],num[1000005][5];
int fa(int x)
{
	if(f[x]==x)return f[x];
	int t=f[x];
	f[x]=fa(f[x]);
	r[x]=(r[t]+r[x])%2;
	return f[x];
}
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)f[i]=i;
	for(int i=1;i<=n;i++)
	{
		int a,opt;
		scanf("%d%d",&a,&opt);
		if(fa(i)!=fa(a))
		{
			int tmp=fa(i);
			f[fa(i)]=fa(a);
			r[tmp]=(r[i]+opt+1+r[a])%2;
		}else
		{
			if((r[i]+r[a])%2!=(opt+1)%2)
			{
				printf("No answer");
				return 0;
			}
		}	
	}
	for(int i=1;i<=n;i++)
	{
		if(fa(i)==i)
		{
			num[i][0]++;
		}else
		{
			num[fa(i)][r[i]]++;
		}
	}
	int maxans=0,minans=0,tot=1;
	for(int i=1;i<=n;i++)
	{
		if(fa(i)==i)
		{
			tot=tot*2%998244353;
			maxans+=max(num[i][1],num[i][0]);
			minans+=min(num[i][1],num[i][0]);
		}	
	}
	printf("%d\n%d\n%d\n",tot,maxans,minans);
	return 0;
}

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值