您的位置:首页 > 其它

3770: 疯狂的限制 乱搞

2016-04-14 07:56 211 查看
从左到右枚举子串的右区间,同时维护每个位置作为子串的左区间时能满足的限制条件个数,将满足条件个数在[L,R]的左区间统计入答案即可。

设当前枚举的右区间为i,位置x作为左区间时满足的限制条件个数为a[x]。

现在将右区间改为i+1,如何维护a[]:

首先a[i+1]可以O(k)判断。

对于某个限定条件c[x]、l[x]、r[x],其需要修改的a[]最多2段:

1. 若S[i+1]=c[x],那么从 倒数第r[x]+2次出现的c[x]的位置+1 到 倒数第r[x]+1次出现的c[x]的位置 的a[]需要-1。

2. 若S[i+1]=c[x],那么从 倒数第l[x]+1次出现的c[x]的位置+1 到 倒数第l[x]次出现的c[x]的位置 的a[]需要+1。

易于发现,对于每个限制条件而言,所需要修改的位置是单调递增的,所以对一个限制条件而言,其所需要修改的位置数总和不超过O(|s|),记录每个字符的出现位置即可做到线性修改。

所以总修改次数为O(k|s|),暴力修改就好了。

居然卡到了BZOJ rank1

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;

const int N=100005;
int cnt1,cnt2,cnt3,cnt4;
int k,L,R,n,base,now;
char s
;
int c
,l[505],r[505],sum[26];
long long ans;
struct node
{
int l,r,id;
};
vector<node> a[26];

int main()
{
//  freopen("letter.in","r",stdin);
//  freopen("letter.out","w",stdout);

scanf("%s",s+1);
n=strlen(s+1);
scanf("%d%d%d",&k,&L,&R);
for (int i=1;i<=n;i++)
s[i]-='a';
for (int i=1;i<=k;i++)
{
char ch[5];
int l,r;
scanf("%s%d%d",ch,&l,&r);
a[ch[0]-'a'].push_back((node){l,r,i});
if (!l) ++base;
}
for (int i=1;i<=n;i++)
{
sum[s[i]]++;
c[i]=base;
if (c[i]>=L&&c[i]<=R) now++;
for (int o=0;o<a[s[i]].size();o++)
{
node j=a[s[i]][o];
if (sum[s[i]]>=j.l&&j.l!=0)
while (1)
{
c[++l[j.id]]++;
if (c[l[j.id]]==L) now++;
else if (c[l[j.id]]==R+1) now--;
if (s[i]==s[l[j.id]]) break;
}
if (sum[s[i]]>j.r)
while (1)
{
c[++r[j.id]]--;
if (c[r[j.id]]==R) now++;
else if (c[r[j.id]]==L-1) now--;
if (s[i]==s[r[j.id]]) break;
}
}
ans+=now;
}
cout << ans << endl;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: