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\) 大于基准加上奇链数量加点数也无解,这是显然的

答案即为:

\[\binom{奇链数量}{m-基准}\cdot 2^{偶链数量} \]

观察到这个质数就是给 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;
}
posted @   Rolling_star  阅读(64)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示