CF1097H

题意

洛谷

做法

\(f_{i,j}\)为第i轮得到的序列,假设j后的状态,状态维护序列的答案,\(pre_i,suf_i\)

\(suf_i\):其长度为\(i\)的后缀是否能成为可行串长度为\(i\)的前缀
\(pre_i\):其长度为\(n-i\)的前缀是否能成为可行串长度为\(n-i\)的后缀

合并类似于线段树,然后\(pre,suf\)用bitset优化

code

这里copy来的

#include<bits/stdc++.h>
using namespace std;

#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i--)
#define rvc(i,S) for(register int i = 0 ; i < (int)S.size() ; i++)
#define rvcd(i,S) for(register int i = ((int)S.size()) - 1 ; i >= 0 ; i--)
#define fore(i,x)for (register int i = head[x] ; i ; i = e[i].next)
#define forup(i,l,r) for (register int i = l ; i <= r ; i += lowbit(i))
#define fordown(i,id) for (register int i = id ; i ; i -= lowbit(i))
#define pb push_back
#define prev prev_
#define stack stack_
#define mp make_pair
#define fi first
#define se second
#define lowbit(x) (x&(-x))

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int,int> pr;

const ll inf = 2e18;
const int N = 3e4 + 10;
const int maxn = 2000020;
const ll mod = 1e9 + 7;

struct node{
	ll num,len;
	bitset <N> pre,suf;
	node() { num = len = 0; pre.reset(),suf.reset(); }
}f[70][62];
int gen[120],n,m,d,B[N];
ll l,r,pow_[70];
int a[maxn];
bitset <N> all;

node merge(const node &a,const node &b){
	if ( !a.len ) return b;
	node res;
	res.len = a.len + b.len;
	res.pre = a.pre , res.suf = b.suf; //pre和suf中包含了长度1..n-1的所有信息,所以合并的时候直接&就好
	res.num = a.num + b.num;
	if ( a.len <= n - 2 ) res.pre &= (b.pre >> a.len) | (all << (n - 1 - a.len)); 
	if ( b.len <= n - 2 ) res.suf &= (a.suf << b.len) | (all >> (n - 1 - b.len));
	if ( res.len >= n ){
		bitset<N> tmp = a.suf & b.pre;
		if ( a.len <= n - 2 ) tmp &= all >> (n - 1 - a.len); //如果a的长度不足,则删除高位
		if ( b.len <= n - 2 ) tmp &= all << (n - 1 - b.len); //删除低位
		res.num += tmp.count();
	}
	return res;
}
void init(){
	pow_[0] = 1;
	rep(i,1,64) pow_[i] = pow_[i - 1] * d;

	int c = 0;
	while ( pow_[c + 1] < r ) c++;
	rep(i,1,n - 1) all[i] = 1;
	rep(j,0,m){
		f[0][j].len = 1;
		if ( n == 1 ) f[0][j].num = j <= B[1];
		else{
			//把没有出现的位置看成0
			//这样便于合并、否则每次合并还需要check整段是否合法
			rep(k,1,n){
				f[0][j].pre[k - 1] = j <= B[k];
				f[0][j].suf[k] = j <= B[k];
			}
		}
	}
	rep(i,1,c){
		rep(j,0,m){
			rep(k,0,d - 1){
				f[i][j] = merge(f[i][j],f[i - 1][(j + gen[k]) % m]);
			}
		}
	}
}
ll solve(ll n){
	int c = 0;
	while ( pow_[c + 1] < n ) c++;
	int add = 0; node res;
	repd(i,c,0){
		rep(j,0,d - 1){
			if ( n >= pow_[i] ){
				n -= pow_[i];
				res = merge(res,f[i][(gen[j] + add) % m]);
			}
			else{
				add += gen[j];
				break;
			}
		}
	}
	cout<<res.num<<endl;
	return res.num;
}
int main(){
	scanf("%d %d",&d,&m);
	rep(i,0,d - 1) scanf("%d",&gen[i]);
	scanf("%d",&n);
	rep(i,1,n) scanf("%d",&B[i]);
	cin>>l>>r;
	init();
	cout<<solve(r) - solve(l + n - 2)<<endl;
}

题外话

降智...

猜你喜欢

转载自www.cnblogs.com/Grice/p/12897983.html
今日推荐