CSP 模拟 7
T1 卷
简单题,比较转化为 \(\log\) 比较,然后就是没有上司的舞会
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define double long double
#define N 200005
using namespace std;
const int p=1e9+7;
const double eps=1e-6;
int n,w[N],g[N][2];
double f[N][2];
int tot,head[N];
struct edge{
int v,nxt;
}e[N*2];
inline void add(int u,int v){
e[++tot].v=v;
e[tot].nxt=head[u];
head[u]=tot;
}
void dfs(int u,int fa){
f[u][1]=log2l(w[u]);g[u][1]=w[u];g[u][0]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].v;
if(v==fa) continue;
dfs(v,u);
f[u][0]+=max(f[v][1],f[v][0]);
f[u][1]+=f[v][0];
if(f[v][1]-f[v][0]>eps) g[u][0]=g[u][0]*g[v][1]%p;
else g[u][0]=g[u][0]*g[v][0]%p;
g[u][1]=g[u][1]*g[v][0]%p;
}
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++) scanf("%lld",w+i);
for(int i=1;i<n;i++){
int u,v;
scanf("%lld%lld",&u,&v);
add(u,v);add(v,u);
}
dfs(1,0);
printf("%lld\n",(f[1][1]>f[1][0])?g[1][1]:g[1][0]);
}
T2 简单题
简单题,直接把考场上思路粘过来就不写了:
可以将问题转化为:
集合中每个元素 \(x\) 向 \(2x\) 连边(不存在集合中则不连)
问题转化为在这个图上黑白染色,使得只有不同色之间有连边,也就是二分图的模型,但这个简易的图明显不需要什么二分图算法
发现一定是一堆链和一堆点,同一条链上的点都为 \(p\cdot 2^k\) 的形式,其中 \(p\) 为奇数,如果又在一条链上,说明有连边,所以 \(p\le \frac n2\),如果是点的话,说明不会连边,而且不会被连边,所以满足 \(\ge \frac n2\),而且是奇数
到这里其实弱化版已经出来了:就是 2 的链的个数和点的个数和 次方
观察到只有长度为奇数的链和点对集合大小产生影响
定义基准为所有链的长度除以 2 向下取整再求和,如果 \(m\) 小于基准无解,如果 \(m\) 大于基准加上奇链数量加点数也无解,这是显然的
答案即为:
观察到这个质数就是给 lucas 开路的,直接 lucas 即可,时间复杂度 \(O(p+q\log n)\)
考虑如何统计奇链数量,算法必须是 \(O(1)\) 或 \(O(\log n)\) 的,启发我们统计 \(p\cdot 4^k\) 的数,即为奇链,所以就是每次将 \(n\) 除以 4 递归,然后找到 \(\displaystyle\left(\frac n2,n\right]\) 中的奇数个数求和即可,偶链即为找到 \(\displaystyle\left(\frac n4,\frac n2\right]\) 的奇数个数求和,最后发现其实点可以归约到奇链的情况
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int p=1e7+19;
int n,m,q,basis,tot,cnt;
int fac[p],inv[p];
int qpow(int a,int b){
int ans=1;
while(b){
if(b&1) ans=ans*a%p;
a=a*a%p;
b>>=1;
}
return ans;
}
int odd(int x){
if(x%2==0) return x/2;
else return x/2+1;
}
int c(int n,int m){
if(m>n) return 0;
return fac[n]*inv[n-m]%p*inv[m]%p;
}
int lucas(int n,int m){
if(!m) return 1;
return c(n%p,m%p)*lucas(n/p,m/p)%p;
}
signed main(){
// freopen("test.out","w",stdout);
cin>>n>>q;
fac[0]=1;
for(int i=1;i<p;i++)
fac[i]=fac[i-1]*i%p;
inv[p-1]=qpow(fac[p-1],p-2);
for(int i=p-2;i>=0;i--)
inv[i]=inv[i+1]*(i+1)%p;
int tmp=n,temp=1;
while(tmp){
tot+=odd(tmp)-odd(tmp/2);
basis+=(temp/2)*(odd(tmp)-odd(tmp/2));
tmp/=4;temp+=2;
}
tmp=n,temp=2;
while(tmp){
cnt+=odd(tmp/2)-odd(tmp/4);
basis+=(temp/2)*(odd(tmp/2)-odd(tmp/4));
tmp/=4;temp+=2;
}
cnt=qpow(2,cnt);
// cout<<basis<<" "<<tot<<endl;
while(q-->0){
scanf("%lld",&m);
if(m<basis||m>basis+tot){
printf("0\n");
continue;
}
printf("%lld\n",lucas(tot,m-basis)*cnt%p);
}
}
T3 粉丝
和 义 差不多,只是把答案化为 \(y+1\sim n\) 和 \(x\sim n\) 做差分即可,还不用考虑选的个数问题
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define N 100005
using namespace std;
int x,y,n,p,ans;
int f[N],g[N],sum[N];
int solve(int liml){
if(liml>n) return 0;
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
memset(sum,0,sizeof(sum));
int B=max(liml,(int)sqrtl(n));
f[0]=g[0]=sum[0]=1;
for(int i=liml;i<B;i++)
for(int j=i;j<=n;j++)
f[j]=(f[j]+f[j-i])%p;
for(int i=1;i<=n/B;i++){
for(int j=i;j<=n;j++)
g[j]=(g[j]+g[j-i])%p;
for(int j=0;j+i*B<=n;j++)
sum[j+i*B]=(sum[j+i*B]+g[j])%p;
}
int tmp=0;
for(int i=0;i<=n;i++)
tmp=(tmp+sum[i]*f[n-i]%p)%p;
return tmp;
}
signed main(){
cin>>x>>y>>n>>p;
cout<<(solve(x)-solve(y+1)+p)%p<<endl;
}
T4 字符串
先将两边相等的部分消去,一定是形如:\(A+B+C+D\) 形式的,合法的方案为 \(A+C\) 或 \(B+D\)
只考虑 \(A+C\) 情况,\(B+D\) 情况将原串反转即得
观察到 \(C\) 一定是 \(P+reverse(A)\) 的情况,其中 \(P\) 是一个回文串,\(reverse(A)\) 是 \(A\) 串的反转,所以就是对于一个 \(P\) 来说,尽量匹配较长的与右边对称的字符串
那么我们将原串反转接到原串后边,形如:\(A+B+C+D+reverse(D)+reverse(C)+reverse(B)+reverse(A)\),所以再倒着跑一边 kmp,对于每个回文中心,找到最右边端点的 \(nxt\) 则为最长匹配的 \(reverse(A)\),对答案贡献为 \(nxt\) 乘二加上回文串长度
点击查看代码
#include<bits/stdc++.h>
#define N 5000005
using namespace std;
int n,p[N<<1],len,tot,ans;
int nxt[N<<1],mnxt[N<<1],lenk;
char s[N],S[N<<1],ss[N<<1];
void Manacher(){
S[0]='[';S[len=1]='|';
for(int i=1;i<=n;i++) S[++len]=s[i],S[++len]='|';
S[++len]=']';
memset(p,0,sizeof(p));
for(int i=1,r=0,d=0;i<len;i++){
if(i>r) p[i]=1; else p[i]=min(p[2*d-i],r-i+1);
while(S[i-p[i]]==S[i+p[i]]) p[i]++;
if(i+p[i]-1>r) d=i,r=i+p[i]-1;
}
// cerr<<"Manacher\n";
}
void Kmp(){
lenk=0;
for(int i=1;i<=n;i++)
ss[++lenk]=s[i];
ss[++lenk]='|';
for(int i=n;i>=1;i--)
ss[++lenk]=s[i];
memset(nxt,0,sizeof(nxt));
memset(mnxt,0,sizeof(mnxt));
for(int i=lenk-1;i>=1;i--){
int tmp=nxt[i+1];
while(tmp&&ss[i]!=ss[lenk-tmp]) tmp=nxt[tmp];
if(ss[i]==ss[lenk-tmp]) tmp++;
nxt[i]=tmp;
}
for(int i=n;i>=1;i--) mnxt[i]=max(mnxt[i+1],nxt[i]);
// cerr<<"Kmp\n";
}
void solve(){
for(int i=1;i<len;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(mnxt[r]>=l) ans=max(ans,p[i]-1+2*l);
}
// cerr<<"Solve\n";
}
// 爆零就爆零,天天好心情
int main(){
scanf("%s",s+1);
n=strlen(s+1);
for(int i=1;i<=n/2;i++)
if(s[i]==s[n-i+1]) tot++;
else break;
if(tot==n/2){printf("%d",n);return 0;}
n-=tot*2;
for(int i=1;i<=n;i++)
s[i]=s[i+tot];
Manacher();Kmp();solve();
reverse(s+1,s+1+n);
Manacher();Kmp();solve();
cout<<tot*2+ans;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?