CSP模拟7
保龄了!!!!保龄了!!!!
A.卷
一眼树形DP板子,一个点选与不选。然后就挂了……
取模会使一个大数变小。所以dp里记录的值无意义。我们可以把相乘变成对数相加的形式。比较对数就好。对数数组要double。
B.简单题
组合数学。
我们向限制连边变成一条链。
$ 1 \rightarrow 2 \rightarrow 4 \rightarrow8 …… $
$ 3 \rightarrow 6 \rightarrow 12 …… $
$ 5 \rightarrow 10 …… $
我们在这一条链上A,B集合是隔一个选一个的。
然后我们可以发现用 $ 2^xp (p\in奇数) $ 可以构造出所有数,不会有漏掉的数。
考虑每条链的贡献。
我们可以发现当链长为奇数和为偶数的情况贡献是不一样的。
当为偶数时就是一半一半两种情况贡献为2。
当为奇数时一个集合是$ len $ 一个是 $ len+1 $。A的可行大小一定是有一定范围 $ [l,r] $ 而 $ l $ 之上的贡献一定有奇链贡献而成的直接组合数 $ C _{奇链总数} ^{m-l} $ 组合数较大可用lucas
Code
#include<cstdio>
#include<cmath>
#define int long long
using namespace std;
const int mod=10000019;
const int N=10000019+5;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
int ksm(int a,int b){
int ans=1;
while(b){
if(b&1)
ans=ans%mod*a%mod;
a=a%mod*a%mod;
b>>=1;
}
return ans%mod;
}
int fac[N+5];
int C(int n,int m){
if(n<m)
return 0;
return fac[n]%mod*ksm(fac[m],mod-2)%mod*ksm(fac[n-m],mod-2)%mod;
}
int lucas(int n,int m){
if(n==m)
return 1;
if(n<m)
return 0;
return lucas(n/mod,m/mod)%mod*C(n%mod,m%mod)%mod;
}
int n,Q;
signed main(){
n=read(),Q=read();
fac[0]=1;
for(int i=1;i<=mod-1;i++){
fac[i]=fac[i-1]%mod*i%mod;
}
int l=0,r=0,tot1=0,tot2=0;
for(int i=1;i<=(int)log2(n)+1;i++){
int p1=n/pow(2,i)+1,p2=(n/pow(2,i-1));
if(p1&1)
p1--;
if(p2&1)
p2++;
// printf("%lld %lld\n",p1,p2);
int num=(p2-p1)/2;
if(i&1)
tot1+=num;
else
tot2+=num;
l+=num*(i/2);
}
r=l+tot1;
int ans=ksm(2,tot2)%mod;
while(Q--){
int m=read();
if(m<l||m>r){
printf("0\n");
continue;
}
printf("%lld\n",ans%mod*lucas(tot1,m-l)%mod);
}
return 0;
}
C.粉丝
同样的DP第二次还是不会……

D.字符串
好神奇的题。
首先我们可以把两端回文的都删掉 $ qjdddqqqdjq \rightarrow ddqqq $
然后考虑剩下的可以是 $ A'+B+C+D 或 B+C+D+E' $ 两种情况。两种情况是类似的所以我们正过来反过去比两遍就行。
考虑如何处理剩余的串。发现剩下的串中,有一段连续回文串,有一段与 $ A' $ 构成回文串。我们可以用马拉车算出其中回文半径并进行扩展。把串反过来接在后面跑KMP。不断更新答案。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=5000005;
char s[N];
int len;
int ans;
char a[N<<1],b[N<<1];
int nxt[N<<1],mxn[N<<1];
int p[N<<1];
inline void work(){
memset(p,0,sizeof(p));
memset(nxt,0,sizeof(nxt));
memset(mxn,0,sizeof(mxn));
int n=(len<<1)+1;
// for(int i=1;i<=len;i++){
// printf("%c",s[i]);
// }
// printf("\n");
a[0]='$';
for(int i=1;i<=len;i++){
a[i*2-1]='#';
a[i*2]=s[i];
}
a[len*2+1]='#';
// for(int i=1;i<=n;i++){
// printf("%c",a[i]);
// }
// printf("\n");
// for(int i=1;i<=n;i++){
// printf("%c",a[i]);
// }
// printf("\n");
int r=0,pos=0;
for(int i=1;i<=n;i++){
if(i<r)
p[i]=min(p[pos*2-i],r-i);
else
p[i]=1;
while(a[i+p[i]]==a[i-p[i]])
++p[i];
if(r<i+p[i]){
r=i+p[i];
pos=i;
}
}
// for(int i=1;i<=n;i++){
// printf("%d ",p[i]);
// }
// printf("\n");
for(int i=1;i<=len;i++)
b[i]=b[len*2-i+1]=s[i];
n=len*2;
for(int i=n-1,j=0;i>=1;i--){
while(j&&b[i]!=b[n-j])
j=nxt[j];
if(b[i]==b[n-j])
j++;
nxt[i]=j;
}
for(int i=len;i>=1;i--)
mxn[i]=max(mxn[i+1],nxt[i]);
// 用来判断是否会重叠
for(int i=1;i<=n;i++){
int l=(i-p[i])/2,r=(i+p[i])/2;
if(nxt[r]<=l)
ans=max(ans,p[i]-1+2*nxt[r]);
if(mxn[r]>=l) // 重叠
ans=max(ans,p[i]-1+2*l);
}
}
int main(){
// freopen("4.in","r",stdin);
// freopen("4.out","w",stdout);
scanf("%s",s+1);
len=strlen(s+1);
int pos1=1,pos2=len;
// for(int i=1;i<=len;i++){
// printf("%c",s[i]);
// }
while(pos1<=pos2){
if(s[pos1]!=s[pos2])
break;
pos1++,pos2--;
}
--pos1;
len-=pos1*2;
for(int i=1;i<=len;i++)
s[i]=s[i+pos1];
// for(int i=1;i<=len;i++)
// printf("%c",s[i]);
// printf("\n");
// printf("%d ",len);
work();
reverse(s+1,s+1+len);
work();
// printf("%d",pos1);
printf("%d",ans+pos1*2);
return 0;
}
我们去掉冗余的东西再算的,再想到剩下的可以是 $ A'+B+C+D 或 B+C+D+E' $ 两种情况。就好做多了。就算知道也想不到KMP。
mxn 数组很神秘。
浙公网安备 33010602011771号