题目链接
https://www.lydsy.com/JudgeOnline/problem.php?id=5443
题目大意
令两个序列为-相似,当且仅当为两个序列对应位置上不同的值的个数,例如1 2 3 4
与1 3 3 3
为2-相似,因为两个序列2位置与4位置是不同的。现有一个长度为的序列,可以将它划分为长度为的个子串。组询问,求每个子串与多少个其他子串为相似。
,空间32MB。
题解
考虑不卡空间的做法,令f[i][j]
表示子串与其他子串有多少个-相似的。转移时枚举两个子串开始位置的间隔,若开始位置+1,那么相似-原开始位置是不是相同的+新结束位置是不是相同的。最后前缀和求的和
现在考虑卡空间的做法,如果询问的是2,6,8
,那么一个子串与另一个子串4-相似,实际上只会对询问6,8产生影响,离线存下所有询问,修改操作就直接找到第一个它的询问,在询问上面修改,最后还是前缀和。
代码
#include <cstdio>
#include <algorithm>
int read()
{
int x=0,f=1;
char ch=getchar();
while((ch<'0')||(ch>'9'))
{
if(ch=='-')
{
f=-f;
}
ch=getchar();
}
while((ch>='0')&&(ch<='9'))
{
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
int print(int x)
{
if(x<0)
{
putchar('-');
x=-x;
}
if(x/10)
{
print(x/10);
}
putchar('0'+x%10);
return 0;
}
const int maxn=10000;
const int maxq=100;
struct query
{
int val,pre_id,ans_id;
bool operator <(const query &other) const
{
return val<other.val;
}
};
bool cmp(const query &a,const query &b)
{
return a.pre_id<b.pre_id;
}
query q[maxq+10];
int ans[maxq+10][maxn+10],ak[maxq+10],v[maxn+10],n,l,m,pos[maxn+10];
int main()
{
n=read();
l=read();
for(int i=1; i<=n; ++i)
{
v[i]=read();
}
m=read();
for(int i=1; i<=m; ++i)
{
q[i].val=read();
q[i].pre_id=i;
}
std::sort(q+1,q+m+1);
for(int i=1; i<=m; ++i)
{
q[i].ans_id=i;
ak[i]=q[i].val;
}
for(int i=0; i<=l; ++i)
{
pos[i]=std::lower_bound(ak+1,ak+m+1,i)-ak;
}
for(int i=1; i<=n-l; ++i)
{
int diff=0;
for(int j=1; j<=l; ++j)
{
if(v[j]!=v[j+i])
{
++diff;
}
}
int p=pos[diff];
++ans[p][1];
++ans[p][1+i];
for(int j=2; j+i+l-1<=n; ++j)
{
diff=diff-(v[j-1]!=v[j-1+i])+(v[j+l-1]!=v[j+i+l-1]);
p=pos[diff];
++ans[p][j];
++ans[p][j+i];
}
}
for(int i=1; i<=m; ++i)
{
for(int j=1; j<=n; ++j)
{
ans[i][j]+=ans[i-1][j];
}
}
std::sort(q+1,q+m+1,cmp);
for(int i=1; i<=m; ++i)
{
for(int j=1; j+l-1<=n; ++j)
{
print(ans[q[i].ans_id][j]);
putchar(' ');
}
putchar('\n');
}
return 0;
}