DTOJ 4020 式神守护

本文探讨了一种复杂的数据结构和算法问题,紫妈(紫)尝试在特定条件下通过隙间的权值召唤式神。文章提出了两种解决方案,一种是基于线段树的50pts解法,另一种是利用分治策略实现的更高效算法。

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

式神守护
【题目背景】
“操纵着大结界的,是紫呢。”
“紫?就是那个一直在隙间里睡觉的那个?”
“她可是具有式神守护着的妖怪哦。”
【题目描述】
(尊重出题人)nnn 个隙间排成一列,每个隙间都有一个权值valival_{i}vali
她可以选出某些隙间来召唤式神:一组隙间能成功召唤式神当且仅当他们的权值和为mmm的倍数。(可以是000 倍)
现在紫 试图召唤QQQ次式神,每次给出一个lil_{i}lirir_{i}ri ,她试图在第lil_{i}lirir_{i}ri个隙间中召唤式神,
她会选择其中一些隙间(不一定需要连续的一些)召唤式神。她想知道,有多少种方案可以
成功召唤式神。
【输入格式】
第一行两个数,nnnmmm
第二行n 个数,表示valival_{i}vali
第三行一个数,表示QQQ
下面QQQ行,每行两个数,表示lil_{i}lirir_{i}ri
【输出格式】
QQQ行,每行一个数,表示方案数,方案数mod(109+7)mod (10^9+ 7 )mod(109+7)输出。
【样例输入】
4 3
5 1 3 2
2
1 2
1 3
【样例输出】
2
4
【数据范围与约定】
对于100%100\%100%的数据,n≤200000,Q≤200000,m≤20,1≤vali≤109n\leq 200000,Q\leq 200000,m\leq 20,1\leq val_{i}\leq 10^9n200000,Q200000,m20,1vali109
数据有梯度。

题解:

骚骚的分治(orzJyt orzSlz)
考虑暴力,设fi,jf_{i,j}fi,j表示前iii个数,构成价值是jjj 的方案数。
于是f0,0=1f_{0,0}=1f0,0=1 , fi,(j+ai)modm+=fi−1,jf_{i,(j+a_{i})modm}+=f_{i-1,j}fi,(j+ai)modm+=fi1,j
然后在考场上,我还想了个nlogn×m2nlogn\times m^2nlogn×m2的线段树,每个节点存下该区间从000m−1m-1m1的方案数,询问的时候将两段区间用m2m^2m2处理出来。
成功拿到50pts50pts50pts
正解:
考虑分治。
将所有区间先提出来,然后对于valival_{i}vali分治,并且带上询问。
考虑分治到[l,r][l,r][l,r],中间是midmidmid
考虑对于询问区间,将r≤midr\leq midrmid 的区间放到[l,mid][l,mid][l,mid]中做,将l≥midl\geq midlmid的区间放到[mid+1,r][mid+1,r][mid+1,r]中做。
只考虑跨过midmidmid的区间。
midmidmid往两边跑上面的暴力DPDPDP
然后直接合并即可。

考场50pts50pts50pts代码:

#include<bits/stdc++.h>
using namespace std;
#define in inline
#define re register
#define rep(i,a,b) for(re int i=a;i<=b;i++)
#define repd(i,a,b) for(re int i=a;i>=b;i--)
#define For(i,a,b) for(re int i=a;i<b;i++)
#define _(d) while(d(isdigit(ch=getchar())))
#define shenben puts("orzlkw");
template<class T>in void g(T&t){T x,f=1;char ch;_(!)ch=='-'?f=-1:f;x=ch-48;_()x=x*10+ch-48;t=f*x;}
typedef long long ll;
const int mod=1e9+7;
const int N=200004;
int n,m,val[N];
struct seg{int v[23];}t[N<<2];
in void mer(seg &x,seg le,seg ri){
	For(i,0,m) x.v[i]=(1ll*le.v[i]+ri.v[i])%mod;
	For(i,0,m){
		For(j,0,m){
			x.v[(i+j)%m]=(x.v[(i+j)%m]+1ll*le.v[i]*ri.v[j]%mod)%mod;
		}
	}
}
in void build(int x,int l,int r){
	if(l==r){t[x].v[val[l]%m]++;return;}
	int mid=l+r>>1;
	build(x<<1,l,mid);build(x<<1|1,mid+1,r);
	mer(t[x],t[x<<1],t[x<<1|1]);
}
in seg Q(int x,int l,int r,int L,int R){
	if(l>=L&&r<=R) return t[x];
	int mid=l+r>>1;
	if(R<=mid) return Q(x<<1,l,mid,L,R);
	else if(L>mid) return Q(x<<1|1,mid+1,r,L,R);
	else{
		seg t1=Q(x<<1,l,mid,L,mid);
		seg t2=Q(x<<1|1,mid+1,r,mid+1,R);
		seg res;mer(res,t1,t2);
		return res;
	}
}
int main(){
	freopen("yukari.in","r",stdin);freopen("yukari.out","w",stdout);
	g(n),g(m);
	rep(i,1,n) g(val[i]);
	build(1,1,n);
	int T;g(T);
	while(T--){
		int l,r;g(l),g(r);
		if(l>r) swap(l,r);
		seg tmp=Q(1,1,n,l,r);
		//For(i,0,m) cout<<tmp.v[i]<<endl;
		printf("%d\n",(1ll*tmp.v[0]%mod+1)%mod);
	}
	return 0;
}

正解:

#include<bits/stdc++.h>
using namespace std;
#define in inline
#define re register
#define rep(i,a,b) for(re int i=a;i<=b;i++)
#define repd(i,a,b) for(re int i=a;i>=b;i--)
#define For(i,a,b) for(re int i=a;i<b;i++)
#define _(d) while(d(isdigit(ch=getchar())))
#define shenben puts("orzlkw");
template<class T>in void g(T&t){T x,f=1;char ch;_(!)ch=='-'?f=-1:f;x=ch-48;_()x=x*10+ch-48;t=f*x;}
const int N=200004,M=30,mod=1e9+7;
struct Q{
	int l,r,id;
}q[N],tmp[N];
int a[N],n,m,T,f[N][M],h[N][M],ans[N];
in void mer(int l,int r,int ql,int qr){
	if(ql>qr) return;
	if(l==r){
		rep(i,ql,qr) ans[q[i].id]++;
		return;
	}
	int mid=l+r>>1;
	int t1=0,t2=0,t3=0;
	rep(i,ql,qr) if(q[i].r<=mid) tmp[++t1]=q[i]; 
	t2=t1; rep(i,ql,qr) if(q[i].l<=mid&&q[i].r>mid) tmp[++t2]=q[i];
	t3=t2; rep(i,ql,qr) if(q[i].l>mid) tmp[++t3]=q[i];
	for(int i=ql,j=1;i<=qr,j<=t3;i++,j++) q[i]=tmp[j];
	mer(l,mid,ql,ql+t1-1);mer(mid+1,r,ql+t2,qr);
	rep(i,l,mid+1) rep(j,0,m) f[i][j]=0;
	rep(i,mid,r) rep(j,0,m) h[i][j]=0;
	f[mid+1][0]=1;h[mid][0]=1;
	repd(i,mid,l){
		For(j,0,m) 
			f[i][(j+a[i])%m]=(f[i+1][j]+f[i][(j+a[i])%m])%mod;
		For(j,0,m) f[i][j]+=f[i+1][j],f[i][j]%=mod;
	}
		
	rep(i,mid+1,r){
		For(j,0,m) 
			h[i][(j+a[i])%m]=(h[i-1][j]+h[i][(j+a[i])%m])%mod; 
		For(j,0,m) h[i][j]+=h[i-1][j],h[i][j]%=mod;
	}
		
	rep(i,ql+t1,ql+t2-1)
		For(j,0,m) 
			ans[q[i].id]=(ans[q[i].id]+1ll*f[q[i].l][j]*h[q[i].r][((m-j)%m+m)%m]%mod)%mod;
}
int main(){
	//freopen(".in","r",stdin);freopen(".out","w",stdout);
	g(n);g(m);
	rep(i,1,n) g(a[i]);
	g(T); rep(i,1,T) g(q[i].l),g(q[i].r),q[i].id=i;
	mer(1,n,1,T);
	rep(i,1,T){
		printf("%d\n",(1ll*(ans[i])%mod+mod)%mod);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

可爱の小公举

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值