P11456 [USACO24DEC] Interstellar Intervals G 题解

dp 优化题,很好的一道题,有紫的难度。

很容易有一个 \(O(n^2)\) 的时间复杂度,可得到 15pts。

但是这转移很抽象了,明显不能用一般的优化。

由于这个区间肯定是偶数的,所以想到这个转移大概和奇偶有关,枚举到奇数和偶数完全不同,奇数和偶数也不相干。
想到可以分析红蓝对答案的限制来求出答案。

对于(第一个)红色,令它的坐标为 \(la\) ,此时的坐标为 \(i\),那我们就可以确定左端点就是一个确定的区间 \([la*2-i,i]\) ,下界是这个点在分割点的时候取到。

对于蓝色,这个区间求起来很麻烦,因为这个第一个蓝色没有太大的关系,既然我们求区间比较麻烦,那我们就可以对这个蓝色所影响的右端点进行操作。因为蓝色只能在后半区,要保证每一个点(\(x\))只能在 \(2*i-x\) 之前才能对答案由贡献。可以用一个临时变量来维护,类似双指针?

总体的代码可以用树状数组来写,代码不难:

#include<bits/stdc++.h>
#define int long long 
using namespace std;
const int N=5e5+5,mod=1e9+7;
int n,dp[N],pre1[N],pre2[N];
void toadd(int &x,int y){
	x+=y;
	if(x>=mod)x-=mod;
	if(x<0)x+=mod;
}
struct BIT{
	int c[N];
	#define lowbit(x) (x&-x)
	void add(int x,int w){
		while(x<=n+1){
			toadd(c[x],w);
			x+=lowbit(x);
		}
	}
	int query(int x){
		int res=0;
		while(x){
			toadd(res,c[x]);
			x-=lowbit(x);
		}
		return res;
	}
	int query(int l,int r){
		if(l>r)return 0;
		return query(r)-query(l-1);
	}
}bit[2];
char s[N];
vector<int>e[N];
signed main(){
	scanf("%lld",&n);
	scanf("%s",s+1);
	dp[0]=1;
	bit[0].add(1,dp[0]);
	int lb=0,la=0;
	for(int i=1;i<=n;i++){
		if(s[i]=='X')dp[i]=dp[i-1];
		else if(s[i]=='R')la=i;
		else {
			while(lb<i){
				if(i+i-lb<=n)e[i+i-lb].push_back(lb);
				lb++;
			}
		}
		for(int c:e[i])bit[c&1].add(c+1,-dp[c]);
		int l= max((la-i+la),0ll);
		toadd(dp[i],bit[i&1].query(l+1,i+1));
		bit[i&1].add(i+1,dp[i]);  
	}
	cout<<dp[n]<<endl;
	return 0;
}

posted @ 2025-05-04 11:01  hnczy  阅读(27)  评论(0)    收藏  举报