A题 Car Show
题意
有n 个数字,并且数字的种类不超过m ,问一共有多少个区间满足区间内m 种数字至少出现1 次。
分析
可以使用双指针来solve 此题l 表示左端点r 表示右端点,我们通过不断将 r 右移,一旦出现区间l−r] 内满足m 个不同种类数出现至少1 次,那我们就不断将 l 右移,直至区间内不满足m 个不同种类的数出现至少1 次。那么每有区间[l,r] 满足 m 个不同种类数出现至少1 次,那么贡献就可以加上n−r+1 。因为[r−n] 区间 无论放几个数都一定满足条件 。
代码如下
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
#define int long long
#define lson 2*p
#define rson 2*p+1
const int N=200005;
const int mod=1e9+7;
int n;
int a[N],cnt[N];
signed main()
{
int t=1;
//cin>>t;
while(t--)
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
if(m==1)
{
cout<<(n)*(n+1)/2;
return 0;
}
int ans=0;
int res=0;
for(int i=1,j=1;i<=n-m+1;)
{
int pos;
while(j<=n)
{
if(!cnt[a[j]])
{
res++;
}
if(j!=pos) cnt[a[j]]++;
if(res==m)
{
pos=j;
break;
}
j++;
}
//cout<<cnt[5]<<endl;
ans+=(n-j+1);
//cout<<i<<" "<<j<<" "<<res<<endl;
//cout<<ans<<endl;
for(int k=i+1;k<=j;k++)
{
if(a[k]!=a[i])
{
cnt[a[i]]--;
if(!cnt[a[i]]) res--;
i=k;
break;
}
else if(a[k]==a[i])
{
if(k!=i&&res==m) ans+=(n-j+1);
cnt[a[i]]--;
if(!cnt[a[i]]) res--;
}
}
// cout<<i<<" "<<j<<endl;
// cout<<ans<<endl;
//j++;
}
cout<<ans<<endl;
}
}
B题 Two Frogs
题意
有两只青蛙,起始位置在 1 号点,每个点都有一个跳跃区间,跳跃区间内的点落地概率相同,假设 aa[1]=5 ,那么 1 号点到2−6 号点的概率相同,求两只青蛙从 1 跳到 n 号点,所有步数的概率之和。
分析
首先由于青蛙不会呆在原地,所以跳跃次数最多不超过n−1 ,设dp[i][j] 表示到达j 点需要i 步,那么初始状态为dp[0][1]=1 即消耗0 步到达1 的概率1 。
代码如下
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
//#define int long long
#define lson 2*p
#define rson 2*p+1
const int N=200005;
const int mod=998244353;
int a[8005];
int dp[8005][8005];
int sum[8005][8005];
long long inv[8005];
int qpow(int a,int b)
{
if(b==0) return 1;
if(b&1) return a*qpow(a,b-1)%mod;
else
{
int mul=qpow(a,b/2)%mod;
return mul*mul%mod;
}
}
signed main()
{
int n;
cin>>n;
for(int i=1;i<=n-1;i++) cin>>a[i];
inv[1]=1;
for(int i=2;i<=n;i++)
{
inv[i] = (mod - mod / i) * inv[mod % i] % mod;
}
//for(int i=1;i<=n;i++) cout<<inv[i]<<endl;
dp[n][0]=1;
sum[0][n]=1;
for(int i=n-1;i>=1;i--)
{
for(int j=1;j<=n-i;j++)
{
if(j==1)
{
if(i+a[i]>=n)
{
dp[i][j]=dp[n][0]*inv[a[i]]%mod;
}
else dp[i][j]=0;
sum[j][i]=(sum[j][i+1]+dp[i][j])%mod;
}
else
{
dp[i][j]=(dp[i][j]+(sum[j-1][i+1]%mod+mod-sum[j-1][min(i+a[i],n)+1]%mod)%mod*inv[a[i]]%mod)%mod;
dp[i][j]%=mod;
sum[j][i]=(sum[j][i+1]+dp[i][j])%mod;
sum[j][i]%=mod;
}
//printf("dp[%d][%d]=%d\n",i,j,dp[i][j]);
}
}
long long ans=0;
for(int i=1;i<=n;i++) ans=(ans+(long long)dp[1][i]*dp[1][i]%mod)%mod,ans%=mod;
cout<<ans<<endl;
}
G题 Magic Spells
题意
给定不超过 5个字符串,统计这些字符串中公有的不同回文串数量,下标不同若回文串相同认为是同一个回文串。
分析
manacher 用来统计每个字符串中的回文子串的左右端点,hash 用于O(1) 查询该回文子串是否出现,并放入 set进行记录。最终如果一个回文子串出现个数和字符串个数相同那么说明该回文子串在所有字符串中皆出现过,总贡献+1。前面部分都不是难点,该题毒瘤 难点在于会卡掉ull 自然溢出的情况。因此要开两个hash一个自然取模,另一个设定一个模数例如 1e9+7 ,减少冲突。
代码如下
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<ll,ll>
typedef unsigned long long ull;
const int N = 6e5+10,P=131;
const ull mod = 1e9+7;
ull h1[N],h2[N],p1[N],p2[N];
map<pair<ull,ull>,int>v;
set<pair<ull,ull>>s;
set<pair<int,int>>v2;
char a[N];
char b[N];
int p[N];
int res;
int t;
int n;
pair<ull,ull> find(int l,int r){//降低冲突
return {(h1[r]-h1[l-1]*p1[r-l+1]%mod+mod)%mod,(h2[r]-h2[l-1]*p2[r-l+1])};
}
void manacher(){
int mr=0,mid;
memset(p,0,sizeof p);
for(int i=1;i<n;i++){//首尾是$^
if(i<mr)p[i]=min(p[mid*2-i],mr-i);//p[mid*2-i]就是j
else p[i]=1;
int l=i-p[i]+1,r=i+p[i]-1;
while(b[i-p[i]]==b[i+p[i]]){
p[i]++;
if(p[i]<2)continue;
int l=i-p[i]+1,r=i+p[i]-1;
if(b[l]!='#') continue;
v2.insert({l,r});//当前字符串的回文子串左右端点
}
if(i+p[i]>mr){
mr=i+p[i];
mid=i;
}
}
}
void init(){
int k=0;
b[k++]='$';b[k++]='#';
for(int i=0;i<n;i++)b[k++]=a[i],b[k++]='#';
b[k++]='^';
n=k;
s.clear();v2.clear();
}
signed main(){
scanf("%d",&t);
for(int tt=1;tt<=t;tt++){
scanf("%s",a);
n=strlen(a);
init();
manacher();
p1[0]=1;p2[0]=1;
for(int i=1;i<n-1;i++){
h1[i]=(h1[i-1]*P+b[i]-'#')%mod;//mod 1e9+7
p1[i]=(p1[i-1]*P)%mod;
}
for(int i=1;i<n-1;i++){
h2[i]=(h2[i-1]*P+b[i]-'#');
p2[i]=(p2[i-1]*P);
}
for(auto x:v2){
int l=x.first,r=x.second;
auto get=find(l,r);
s.insert(get);
}
for(auto x:s){
v[x]++;
if(tt==t&&v[x]==t)res++;
}
for(int i=0;i<=n;i++)a[i]='\0',b[i]='\0';
}
cout<<res<<endl;
return 0;
}