石
拜谢 @happy_zero 大神提供的单 log 做法。
先将修改差分处理,形成若干连续段。
考虑答案的形态。由于是给定子串的一个长度为
当
考虑如何找到这个后缀。由于子串内
如果直接二分后缀,需要再二分找到其属于哪个连续段,复杂度达到线性对数平方。
这时候我们直接二分后缀所属连续段,然后就可以
::::success[代码]
#include <bits/stdc++.h>
using std::cin;
typedef long long ll;
constexpr int N=2e5+114,mod=1e9+7;
int n,m,q;
std::map<int,int> c; // 端点
std::vector<int> v,s; // 端点、前缀 0 数量
std::vector<ll> p; // 前缀和
int t;
ll qpow(ll x,ll y)
{
ll r=1;
for(;y;x=1ll*x*x%mod,y/=2)if(y&1)r=1ll*r*x%mod;
return r;
}
int main()
{
cin.tie(nullptr)->sync_with_stdio(false);
cin>>n>>m>>q;
c[1]=c[n+1]=2;
for(int i=1,l,r;i<=m;i++)
{
cin>>l>>r;
c[l]^=1,c[r+1]^=1;
}
t=c[1]&1; // 第一块颜色
for(auto [x,y]:c)if(y)
{
v.push_back(x);
}
s.resize(v.size()),p.resize(v.size());
if(t)s[0]=0,p[0]=(qpow(2,v[1]-v[0])+mod-1)%mod;
else s[0]=v[1]-v[0],p[0]=0;
for(int i=1,j=t^1;i<v.size()-1;i++,j^=1) // 计算前缀 0 数量和前缀和
{
if(j)
{
s[i]=s[i-1];
p[i]=(1ll*(p[i-1]+1)*qpow(2,v[i+1]-v[i])+mod-1)%mod;
}
else
{
s[i]=s[i-1]+v[i+1]-v[i];
p[i]=1ll*p[i-1]*qpow(2,v[i+1]-v[i])%mod;
}
}
// 属于哪块
auto bel=[](int x){return std::upper_bound(v.begin(),v.end(),x)-v.begin()-1;};
for(int l,r,k;q--;)
{
cin>>l>>r>>k,l--;
int L=bel(l),R=bel(r);
int lc1=~L?(t^L&1?l-s[L]:v[L+1]-1-s[L]):0; // l 前缀 1 数量
int rc1=t^R&1?r-s[R]:v[R+1]-1-s[R]; // r 前缀 1 数量
int c1=rc1-lc1; // [l,r] 1 数量
int n0=k-c1; // 需要的 0 的数量
if(n0<=0) // 不需要 0
{
printf("%d\n",(qpow(2,k)+mod-1)%mod);
}
else
{
int ql=std::max(L,0)-1,qr=R,rc0=r-rc1,mid;
while(ql+1<qr) // 二分分界点在那一块
{
mid=(ql+qr)/2;
(s[mid]>=rc0-n0?qr:ql)=mid;
}
// 分界点
int p0=v[qr+1]-1,t0=n0-(rc0-s[qr]),p1=p0-t0;
int len=r-p1,l1=k-len;
printf("%d\n",((1ll*(qpow(2,l1)+mod-1)*qpow(2,len)+ // 前面的 1
1ll*(R?p[R-1]:0)*qpow(2,r-v[R]+1)+ // r 整块前缀和
(t^R&1?qpow(2,r-v[R]+1)+mod-1:0) // r 散块前缀和
-1ll*(qr?p[qr-1]*qpow(2,v[qr]-1-r):0) // 整块前缀和
-1ll*(t^qr&1?qpow(2,p1-v[qr]+1)+mod-1:0)*qpow(2,p1-r) // r 散块前缀和
)%mod+mod)%mod);
}
}
}
::::
以防有人不知道怎么下数据。