成外NOIP2019模拟1

成外NOIP2019模拟1


T1 

大意:给你一个长度$\leq 300$的字符串,求出这个字符串所有子序列的价值和,一个串的价值为所有长度不大于$\frac{len+1}{2}$的$border$的长度之和

题解:

  • 由于有了长度限制的条件,那么$border$的重叠位置只能最多为一,给人一种可以枚举其中的分界线的感觉,于是我们枚举$border$右边的第一个位置$p$
  • 把串首串尾的$border$定好了之后中间取或不取随便,会乘上一个2的次幂
  • 设$cnt_{i,j}$表示左边从1到$i$右边从$p$到$j$的两个串的公共子序列对数(对数不是个数,相同但是位置不同算多次),同理设$sum_{i,j}$表示的是...的所有对公共子序列的长度之和
  • 考虑转移,若$s_i==s_j$,那么$sum_{i,j}=\sum_{k<i}\sum_{l<j}(sum_{k,l}+cnt_{k,l})$,因为之前所有的串都能长度加一,$cnt_{i,j}=\sum_{k<i}\sum_{l<j}cnt_{k,l}$
  • 统计答案就是在$s_i==s_j$处,$ans+=sum_{i,j}*2^{\min\{0,p-i-1\}}$
  • 然后前缀和优化一下就能$O(n^3)$
 1 #include<bits/stdc++.h>
 2 #define FOR(i,a,b) for (register ll i=(a);i<=(b);i++)
 3 #define For(i,a,b) for (register ll i=(a);i>=(b);i--)
 4 #define mem(i,j) memset(i,j,sizeof(i))
 5 #define GO(u) for (register ll j=f[u];j!=-1;j=nxt[j])
 6 #define fi first
 7 #define se second
 8 #define pb push_back
 9 #define pii pair<ll,ll>
10 #define MP make_pair
11 using namespace std;
12 typedef long long ll;
13 const ll N=303;
14 const ll mod=998244353;
15 ll n,ans=0,nxt[N],er[N],cnt[N][N],sum[N][N];
16 char s[N],b[N];
17 inline ll read()
18 {
19     ll x=0,f=1;
20     char c=getchar();
21     while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
22     while (c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
23     return f*x;
24 }
25 inline void write(ll x)
26 {
27     if (x<0) putchar('-'),x=-x;
28     if (x>9) write(x/10);
29     putchar(x%10+'0');
30     return;
31 }
32 inline void solve(int head)
33 {
34     FOR(i,1,n) FOR(j,1,n) sum[i][j]=cnt[i][j]=0;
35     FOR(i,1,head) 
36     {
37         if (s[i]==s[head]) cnt[i][head]=sum[i][head]=1;
38         cnt[i][head]=(cnt[i][head]+cnt[i-1][head])%mod;
39         sum[i][head]=(sum[i][head]+sum[i-1][head])%mod;
40     }
41     FOR(i,1,head) FOR(j,head+1,n)
42     {
43         cnt[i][j]=(cnt[i-1][j]+cnt[i][j-1]-cnt[i-1][j-1]+mod)%mod;
44         sum[i][j]=(sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+mod)%mod;
45         if (s[i]==s[j])
46         {
47             cnt[i][j]=(cnt[i][j]+cnt[i-1][j-1])%mod;
48             sum[i][j]=(sum[i][j]+sum[i-1][j-1]+cnt[i-1][j-1])%mod;
49         }
50     }
51     FOR(i,1,head) FOR(j,head,n) if (s[i]==s[j]) ans=(ans+er[max(0*1LL,head-i-1)]*(sum[i][j]-sum[i-1][j]-sum[i][j-1]+sum[i-1][j-1]+mod+mod)%mod)%mod;
52     return;
53 }
54 int main()
55 {
56 //    freopen("clause10.in","r",stdin);
57 //    freopen("myclause.out","w",stdout);
58     scanf("%s",s+1);
59     n=strlen(s+1);
60     er[0]=1;
61     FOR(i,1,n) er[i]=er[i-1]*2%mod;
62     FOR(i,1,n) solve(i);
63     write(ans);putchar('\n');
64     return 0;
65 }
View Code

猜你喜欢

转载自www.cnblogs.com/C-S-X/p/11651034.html