[NOIP提高组]2011年 Day1-T2 选择客栈

本文详细解析了一种组合计数算法,通过动态规划的方式求解特定条件下方案的总数。算法利用前缀和优化判断过程,提高了效率。适用于解决数学竞赛中的组合问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

题目大意

给出n组数,分别放入k个集合,一个合法条件p(>=p 即合法)

每组数包含属于哪个集合以及它是否符合条件

每个集合内的任意两个数所在的位置之间,有一个点合法,则为一种方案

求方案总数

题目解析

f[i]表示 1~i-1 个点有多少个点可以和第 i 个点组成一种方案

从头到尾扫描数组,遇到两个数(k,i)同属一个集合(k,i 在集合内相邻),则判断k~i中是否有数合法
(判断是否合法用了前缀和,判断 k 和 i 两个数的前缀和的差是否大于0)

若有数合法,则 f[i]=num[a[i]],因为k,i在集合内相邻,若k,i可组成方案,则该集合内k之前的数都与i可组成方案
(num[a[i]]表示到 1~i-1中 在第a[i]个集合的个数)

若无数合法,则 f[i]=f[s[a[i]]],因为1~k-1有x个数组成方案,则这x个数都可与后面的i组成方案
(s[a[i]]表示集合a[i]在i之前的一个数,即上文的k)

f[1~n]的和即为所求

代码

#include<bits/stdc++.h>
#define N 200005
using namespace std;
int n,k,p,ans;
int a[N],b[N],flag[N],f[N],s[55],num[55];
int read()
{
	char c=getchar();
	int x=0;
	while(!isdigit(c)) c=getchar();
	while(isdigit(c))
	{
	  x=x*10+c-48;
	  c=getchar();
	}
	return x;
}//快读 
int main()
{
	n=read();k=read();p=read();
	for(int i=1;i<=n;i++)
	{
	  a[i]=read();b[i]=read();
	  if(b[i]<=p)
	   flag[i]=1;//若该点合法,则记为1 
	}
	for(int i=n;i>=1;i--)
	 flag[i]+=flag[i+1];//求第i个点以后有多少个点合法 
	for(int i=1;i<=n;i++)
	{
	  //s[i]表示第a[i]个集合中,i的上一个数 
	  if(s[a[i]]==0)//若i为该集合内的第一个数,则直接跳到下一次 
	  {
	  	s[a[i]]=i;
	  	num[a[i]]++; 
	  	continue;
	  }
	  if(s[a[i]]!=i)
	  {
	  	if(flag[s[a[i]]]>flag[i+1])//利用前缀和是否有差值判断合法 
	  	 f[i]=num[a[i]];
		else
		 f[i]=f[s[a[i]]];
	  	s[a[i]]=i;//更新第a[i]个集合的数,即i 
	  	num[a[i]]++;//集合到目前为止的个数+1 
	  }
	}
	for(int i=1;i<=n;i++)
	 ans+=f[i];
	cout<<ans;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值