SCAU 18936 手串

Description

字节跳动2018校招Android方向(第二批) 
作为一个手串艺人,有金主向你订购了一条包含n个杂色串珠的手串——每个串珠要么无色,要么涂了若干种颜色。
为了使手串的色彩看起来不那么单调,金主要求,手串上的任意一种颜色(不包含无色),在任意连续的m个串珠里至多出现一次(注意这里手串是一个环形)。
手串上的颜色一共有c种。现在按顺时针序告诉你n个串珠的手串上,每个串珠用所包含的颜色分别有哪些。
请你判断该手串上有多少种颜色不符合要求。即询问有多少种颜色在任意连续m个串珠中出现了至少两次。

输入格式

第一行输入n,m,c三个数,用空格隔开。(1 <= n <= 10000, 1 <= m <= 1000, 1 <= c <= 50) 
接下来n行每行的第一个数num_i(0 <= num_i <= c)表示第i颗珠子有多少种颜色。接下来依次读入num_i个数字,每个数字x表示第i颗柱子上包含第x种颜色(1 <= x <= c)

输出格式

一个非负整数,表示该手链上有多少种颜色不符需求。

输入样例

5 2 3
3 1 2 3
0
2 2 3
1 2
1 3

输出样例

2

提示

第一种颜色出现在第1颗串珠,与规则无冲突。
第二种颜色分别出现在第 1,3,4颗串珠,第3颗与第4颗串珠相邻,所以不合要求。
第三种颜色分别出现在第1,3,5颗串珠,第5颗串珠的下一个是第1颗,所以不合要求。
总计有2种颜色的分布是有问题的。 
这里第2颗串珠是透明的。

先上代码


#include<stdio.h>
const int N=1e4+5;
int s[N*2][55];
int n,m,c;
int st[55];

int main()
{
	scanf("%d%d%d",&n,&m,&c);
	for(int i=1;i<=n;i++)
	{
		int x;
		scanf("%d",&s[i][0]);
		for(int j=0;j<s[i][0];j++)
		{
			scanf("%d",&x);
			s[i][x]++;
			s[i+n][x]++;
		}
		for(int j=1;j<=c;j++)s[i][j]+=s[i-1][j];
	}
	
	for(int i=n+1;i<=2*n;i++)
	{
		for(int j=1;j<=c;j++)
		{
			s[i][j]+=s[i-1][j];
		}
	}
	if(m>=n)
	{
		int num=0;
		for(int i=1;i<=c;i++)
		{
			if(s[n][i]>=2)num++;
		}
		printf("%d\n",num);
	}
	else
	{
	    int num=0;
	    for(int i=m;i<n+m;i++)
	    {
		    for(int j=1;j<=c;j++)
		    {
			    if(s[i][j]-s[i-m][j]>=2&&!st[j])
			    {
			        st[j]=1;
			        num++;
			    }
		    }
	    }
	    printf("%d\n",num);
	}
	
	
	return 0;
}

这里先介绍什么是前缀和。

前缀和其实就是高中所学的前n项和,Sn=a1+a2+...+an,那么我们求区间[l,r]中值的和就是前r项和减去前l-1项和(这里涉及到减一,所有建议下标从1开始防止越界)。

那对于这道题,就可以开辟一个二维数组s[i][j]代表前i个串珠中颜色编号为j的出现次数,所以我们要把这个环形手串铺开,变成线性

a36a45c0ba224b1689c4f932b2b36c7e.png

怎么做到像环形一样到末尾又可以到回开始位置?我们可以在数组中再放入相同的n个串珠,这样,我们从下标m(题目要求的连续m个串珠)开始,当到达下标n+m时终止,就可以把所有情况都遍历一遍。

f9cce795ef8740eebd2f58934c3b8481.png

颜色的下标从1开始,那就可以把s[i][0]作为第i个串珠所含的颜色数,后面输入颜色编号为x,就让s[i][x]加1,表明出现过,因为是相同的串珠,所以s[i+n][x]也要加1。

某一个颜色x是否再连续m个珠中出现2次或以上就是求s[i][x]-s[i-m][x]是否大于等于2。

值得注意的是当m>=n时,所有手串都会被选入,这个时候只要看前n个中每个颜色出现次数是否大于等于2就行了,特别判断就行。

 

最后也是最重要的,不符合标准的颜色在被统计后记得加个标记,即st[i]=1,不然后面可能会再加一次就错了(我就被这个给坑了一下)。

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值