HGOI 202311月 && xoj【4834】永恒之夜(night)题面与题解

 

题面

题目描述(就挺抽象的)

feluamn 所在的世界陷入了永恒之夜。本属于光明的他们,如今只能在黑暗中苟且为生。

精灵一族信任与她的善良、勇敢与坚韧,feluamn 得知了划破这黑暗的唯一方法。那就是登上光明之巅!只有 feluamn 这么可爱又善良的女孩子,才有可能成功。

为了拯救这即将凋零的世界,feluamn 整理行囊,准备踏上行程。

feluamn 需要一些魔力晶板来驱散路途的黑暗。现在 feluamnn 块魔力晶板,每块晶板上有 m 个魔力槽,从 1m 标号。一些魔力槽中可能有魔力水晶。feluamn 需要第 a1,a2,,ak 个位置上有魔力水晶,而其他位置没有水晶的魔力晶板。所以她需要取下一些魔力水晶。由于黑暗诅咒的关系,当第 i 个位置的水晶被取下后,第 i+b1,i+b2,,i+bc 个位置上的魔力水晶也会随之掉落。特别的,如果 i+b1,i+b2,,i+bc 的某个位置上没有魔力水晶,这个魔力水晶就不能被取下。并且水晶一旦离开魔力晶板就无法再安装上去了。

feluamn 希望能有尽量多的魔力晶板,以备旅途上的不测。由于空间水晶的存在,feluamn 并不需要担心衣兜不够大。但她忙于将 FMT (鬼知道FMT是什么玩意) 融会贯通,以更好地对付黑暗,并没有时间来考虑这个问题。如果你能告诉她答案,这个可爱又善良的女孩子会对你感激不尽。

输入描述

为了便于输入输出,约定用一个十进制整数来表示魔力晶板的状态。这个整数的二进制下第 i 位为 1 ,则表示魔力晶板的第 i+1 个位置有魔力水晶,反之则没有。

第一行一个正整数 T ,表示数据组数。

对于每组数据,第一行两个正整数 n,m ,意义如问题描述中所述。

接下来 n 行,每行一个整数 x 表示 feluamn 拥有的魔力晶板的状态。

接下来一行一个非负整数 c ,意义如问题描述中所述。

接下来一行 c 个正整数,第 i 个正整数表示 bi 。

接下来一行一个正整数 q ,表示询问数量。接下来 q 行,每行一个非负整数表示 feluamn 所需的魔力晶板的状态。

输出描述

对于每组数据,输出 q 行。第 i 行一个非负整数表示第 i 个询问的答案。注意不同的数据之间不需要用空行隔开。

样例数据

数据范围

对于所有数据,T≤5,1≤q,n≤100000,1≤m≤20,1≤bi≤m,c<m 。保证代表魔力晶板状态的整数不超过 m 位。相同的 bi 只算一个。

对于20% 的数据:1≤q,n≤1000

对于另外20% 的数据:1≤m≤7

对于另外30% 的数据:c=0

对于另外40% 的数据:没有特殊限制

题解

首先,我们可以发现 b 这个数组完全可以用一个十进制数表示(因为这个题目的其他输入都是用十进制数表示的),设这个数为 ans,那么显然有:

$ans =(1«b_{i})$

其中 ans 的初始值为 1。它表示在 1 号位去掉魔力水晶时,实际会去掉的水晶。

但是显然,不只有 1 号位可以进行去掉水晶的操作,任何位置都可以(当然,要合法)。这也简单,只需要将 ans 左移 j 位,就可以表示在 (1+j) 位进行操作。那么,我们让 j0 不断递增,直到不可行为止。

判断不可行的方式是:如果 ans 左移 j 位后比最大的 x 还要大,那么显然不可行,即当:

ans«j>xmax 时结束。

注意,j 必须从小到大,如果从大到小的话会有重复情况。

那么如何判断是否合法?

首先,我们要明确不合法的情况。当那个位置上没有魔力水晶时,你还进行去掉的操作,那就是不合法的。

由于 xans 的二进制中, 1 表示有,而 0 表示没有,所以如果 xans 进行按位与运算,结果仍是 x 的话,就说明 ans 包含于 x ,即合法。即当:

ans&x==ans时合法,否则不合法。

接下来就简单了,令 fi 表示能到状态 i 的魔力晶板个数,那么显然有:

fians«j+1=fians«j+1+fi

最后对于每个询问 qus 直接输出 fqus 即可。

代码中,用 t 数组来表示 ans 左移 j 位 (tj+1=ans«j),tlen 为目前 t 数组的长度,即 j+1 ; maxn 表示 xmaxf 数组的初值为 f[xi]=1 ,其余位置为 0k 表示每个被输入的 bi

好了,请食用下面这份代码:

#include<bits/stdc++.h>
using namespace std;
inline int read()//快读函数,可以快速读取(输入) 
{
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') w=-1;
		ch=getchar();
	} 
	while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
	return s*w;
}
const int N=25;
const int NN=1e6+114514;
int f[NN],t[N];
int main()
{
	int T;
	T=read();
	while(T--)
	{
		int tlen=1;
		memset(f,0,sizeof(f));
		int m,n,q,c,maxn=0,ans=1;
		n=read();
		m=read();
		for(int i=1;i<=n;i++)
		{
			int x;
			x=read();
			f[x]++;
			maxn=max(maxn,x);
		}
		c=read();
		for(int i=1;i<=c;i++)
		{
			int k;
			k=read();
			ans|=(1<<k);
		}
		t[1]=ans;
		while(t[tlen]<=maxn)
		{
			for(int i=maxn;i>=t[tlen];i--)
			{
				if((i&t[tlen])==t[tlen]) f[i-t[tlen]]+=f[i];
			}
			t[tlen+1]=(t[tlen]<<1);
			tlen++;
		}
		q=read();
		for(int i=1;i<=q;i++)
		{
			int qus;
			qus=read();
			printf("%d\n",f[qus]);
		}
	}
}

这道题的出题者实在是天才。

还有人说高维前缀和、FMT 也可以做,就留给大家研究了。