CSP模拟7

考的好像还行,主要是别人T1炸了一片;

A. 卷

题目链接

非常普通的树上dp的板子题,就是把加变成了乘,因为乘这个东西比较的恶心,乘两三下就爆long long,所以我们把乘法改成加法。

根据这个小学二年级的知识:

logab+logac=loga(b*c)

转成加法比较,维护乘法的值。

复杂度O(n)

#include <iostream>
#include <cstdio>
#include <cstring> 
#include <cmath> 

const int MAXN=2e5+10;
const int mod=1e9+7;
const double eps=1e-10;

using namespace std;

int n,cnt;
long long w[MAXN];
struct edge {
    int to,next;
}a[MAXN<<1];
int head[MAXN];
long long f[MAXN][2]; 
long double f1[MAXN][2];

void add(int u,int v) {
    a[++cnt].to=v;
    a[cnt].next=head[u];
    head[u]=cnt; 
}

void dfs(int now,int fa) {
    
    f[now][0]=f[now][1]=1;
    f1[now][0]=f1[now][1]=0;
    
    f[now][1]=w[now]%mod;
    f1[now][1]+=log(w[now]);
    
    for(int i=head[now];i;i=a[i].next) {
        int v=a[i].to;
        if(v==fa) continue;
        dfs(v,now);
        
        f[now][1]=f[now][1]*f[v][0]%mod;
        f1[now][1]=f1[now][1]+f1[v][0];
        if(f1[v][1]-f1[v][0]>eps) {
            f[now][0]=f[now][0]*f[v][1]%mod;
            f1[now][0]=f1[now][0]+f1[v][1];
        }
        else {
            f[now][0]=f[now][0]*f[v][0]%mod;
            f1[now][0]=f1[now][0]+f1[v][0];
        }
    }
    
    
    
    return;
}

int main() {
    
    scanf("%d",&n);
    for(int i=1;i<=n;i++) {
        scanf("%lld",&w[i]);
        
    }
    for(int i=1;i<n;i++) {
        int u,v;
        scanf("%d%d",&u,&v);
        add(u,v);
        add(v,u);
    }
    
    dfs(1,0);
    
    if(f1[1][1]-f1[1][0]>eps) {
        printf("%lld",f[1][1]%mod);
    }
    else {
        printf("%lld",f[1][0]%mod);
    }
    
    return 0;
} 
View Code

 

B. 简单题

题目链接

考虑数是如何分的,我们发现,对于一个奇数 x,我们可以把它想象成一个奇数链,链上的元素为 x,x*2,x*4,x*8.......x*2k(x*2k<=n)

这个链上的元素在A还是在B只与链上的其他元素有关,与其他链上的元素无关。

而且对于每一条链,链内元素谁和谁在一起也是固定的,举个例子:xx*2不能在同一边,x*2x*4不能在同一边,那x与x*4就必然在同一边;

根据这个性质我们发现,对于长度为偶数的链(下面成为偶数链)是对半分的,是能且只能这么分(但不确定这一半是给了A还是B),所以对于单个偶数链,它对答案的贡献为*2;

再看奇数(长度)链,相当于有一半比另一半多一个元素,.A与B被分得的长度也是因为多的这一个元素而不同;

这时我们发现,一个人被分的长度是有范围的,设偶数链个数为a,奇数链个数为b,n就是题目中那个n;

被分得的长度len范围为:[(n-b)/2,(n-b)/2+b];

也就是把最短的都给他,和最长的都给他。

因为长度其实是由奇数链决定的,所以所有奇数链的贡献为 C(b,m-l) (m为题中的m,l是上面的那个极小值)

现在我们就要求出奇数链的个数;

总链数为 (n+1)/2;; 所以让n不断地/2,除一次,记录现在还有多少条链,与下次的相减,就是在这个长度结束的链,在分别记录偶数链和奇数链就可以了。

复杂度O(log n);

#include <iostream>
#include <cstdio>
#include <cmath>

const int mod=10000019;

using namespace std;

inline long long read() {
    long long f=0,x=1;
    char ch=getchar();
    while(ch>'9' || ch<'0') {
        if(ch=='-') {
            f=-1;
        }
        ch=getchar();
    }
    while(ch>='0' && ch<='9') {
        //x=x*10+ch-'0';
        x=(x<<1)+(x<<3)+(ch^48); 
        ch=getchar();
    }
    return x*f;
}

long long n,m,l,r;
int q,sum,lg;
int cnt[10000010];
long long jc[mod<<1],inv[mod<<1],invj[mod<<1],mi[110];

long long C(long long n,long long m) {
    if(m>n) return 0;
    return jc[n]%mod*invj[n-m]%mod*invj[m]%mod;
}

long long Lucas(long long n,long long m) {
    if(!m || n==m) return 1;
    if(m>n) return 0; 
    return Lucas(n/mod,m/mod)%mod*C(n%mod,m%mod)%mod;
}

long long fpow(long long x,int k) {
    long long res=1;
    while(k) {
        if(k&1) res=res*x%mod;
        x=x*x%mod;
        k>>=1; 
    }
    return res%mod;
} 

void init() {
    lg=log2(n)+1;
    
    jc[0]=1,jc[1]=1,inv[1]=1,invj[0]=1,invj[1]=1;
    for(int i=2;i<=mod;i++) {
        jc[i]=jc[i-1]%mod*i%mod;
        inv[i]=(mod-(mod/i))%mod*inv[mod%i]%mod;
        invj[i]=invj[i-1]*inv[i]%mod;
    }
    
    mi[0]=1;
    
    for(int i=1;i<=lg;i++) {
        mi[i]=mi[i-1]*2;
    }
}

int main() {
    
    scanf("%lld%d",&n,&q);
    
    init();
    
    for(int i=0;i<=lg;i++) {
        cnt[i]=(n/mi[i]+1)/2;
    }
    for(int i=0;i<=lg;i++) {
        cnt[i]-=cnt[i+1];
        if((i+1)&1) {
            sum+=cnt[i];
        }
    }
    
    l=(n-sum)>>1;
    r=l+sum;
    
    long long total=(n+1)>>1;
    long long g=fpow(2,total-sum)%mod;
    
    while(q--) {
    //    m=read();
        scanf("%lld",&m);
        if(m<l || m>r)  {
            printf("0\n");
        }
        else {
            printf("%lld\n",g%mod*Lucas(sum,m-l)%mod);
        }
    }
    
    return 0;
}
View Code

C. 粉丝

题目链接

有两种计算划分数的 DP

fi,j表示当前 DP 到数字 i 也就是最大的数字为 i ,总和为 j 的方案数。

转移方程就是:fi,j=fi1,j+fi1,jk(k[1,i])

gi,j表示当前分成了 i 个数字,总和为 j 的方案数

转移方程就是:gi,j=gi1,j+gi,ji

注意上述的所有方程都暂时不考虑起始为 [x,y]的限制。

我们就可以固定下界,上界直接设为 n ,然后就类似与求了一个后缀,减一下就好了。

这两个 dp 分别使用都是 O(n2))的,但是假设我们令B=√n ,把所有 B≤ 的数字都拿出来使用 f 的那个 dp。

那么剩下的数字都必然 >B> ,因此最多被划分成 nB个数字,此时使用 g 来 dp。

最后合并两个 DP 的值,复杂度为 O(n√n)

 

#include <iostream>
#include <cstdio>
#include <cmath> 
#include <cstring> 

const int MAXN=1e5+5; 

using namespace std;

int x,y,n,mod; 
long long f[MAXN],g[400][MAXN];
 
long long solve(int x) {
    memset(f,0,sizeof(f));
    memset(g,0,sizeof(g));
    f[0]=1, g[0][0]=1;
    
    int l1=sqrt(n);
    for(int i=x;i<=l1;i++) {
        for(int j=i;j<=n;j++) {
            f[j]=(f[j]+f[j-i])%mod;
        }
    }
    
    int l2=max(l1,x-1);
    for(int i=0;i<=l1;i++) {
        for(int j=0;j<=n;j++) {
            if(i && i+j<=n) {
                g[i][i+j]=g[i][i+j]+g[i][j];
                g[i][i+j]%=mod;
            } 
            if(j+l2+1<=n) {
                g[i+1][j+l2+1]=g[i+1][j+l2+1]+g[i][j];
                g[i+1][j+l2+1]%=mod;
            } 
        }
    }
    
    long long ans=0;
    for(int i=0;i<=l1;i++) {
        for(int j=0;j<=n;j++) {
            ans=(ans+g[i][j]*f[n-j]%mod)%mod;
        }
    }
    return ans;
}

int main() {
    
    scanf("%d%d%d%d",&x,&y,&n,&mod);
    
    printf("%lld",(solve(x)-solve(y+1)+mod)%mod);
    return 0;
} 
View Code

D. 字符串

题目链接

首先把两边一样的先删去,记录下来。

剩下的为A,B,C,D 或者B,C,D,E;

考虑A,B,C,D,答案为A+C;即使A+C最长;

找出的回文串一定是两种情况;(其中P为回文串)

  1. A=M+P ,P与N之间是没有用的串, C=N ,M=reverse(N)
  2. A=M ,M和P之间是没有用的串, C=P+N, M=reverse(N);

考虑在剩下的字符中用Manacher找以每个字符为中心的最长回文串,在回文串的两头接上最长的M和N;

至于M,N怎么求,先把原串镜像,倒着跑KMP就行了,可以自己手摸一下;

复杂度O(n)

#include <iostream>
#include <cstdio>
#include <cstring> 
#include <algorithm> 

const int MAXN=5e6+5;

using namespace std;

int n,len,ans,fro;
char s[MAXN],s1[MAXN<<1],s2[MAXN<<1];
int p[MAXN<<1],nxt1[MAXN<<1],nxt[MAXN<<1],mnxt[MAXN<<1];

void Manacher() {
    
    memset(p,0,sizeof(p));
    
    len=0;
    int r=0,c=0;
    s1[0]='$'; 
    s1[++len]='#';
    for(int i=1;i<=n;i++) {
        s1[++len]=s[i];
        s1[++len]='#';
    } 
    s1[++len]='*';
    
    for(int i=1;i<len;i++) {
        if(i<r) {
            p[i]=max(p[c<<1-i],p[c]+c-i); 
        }
        else p[i]=1;
        
        while(s1[i+p[i]]==s1[i-p[i]]) {
            p[i]++;
        }
        if(i+p[i]>r) {
            c=i;
            r=p[i];
        }
    } 
}

void Kmp() {
    
    memset(nxt,0,sizeof(nxt));
    memset(mnxt,0,sizeof(mnxt)); 
    
    int len1=0;
    for(int i=1;i<=n;i++) {
        s2[++len1]=s[i];
    }
    s2[++len1]='|'; 
    for(int i=n;i>=1;i--) {
        s2[++len1]=s[i];
    }
    
    /*for(int i=len1-1;i>=1;i--) {
        int j=nxt[i+1];
        while(j && s2[i]!=s2[len1-j]) {
            j=nxt[j];
        } 
        if(s2[i]==s2[len1-j]) {
            j++;
        }
        nxt[i]=j; 
        cout<<nxt[i]<<" "; 
    }
    cout<<endl;*/
    
    for(int i=2,j=0;i<=len1;i++) {
        while(j && s2[i]!=s2[j+1]) {
            j=nxt1[j];
        }
        if(s2[i]==s2[j+1]) {
            j++;
        }
        nxt1[i]=j;
    }
    
    for(int i=1;i<=len1;i++) {
        nxt[i]=nxt1[len1-i+1];
    }
    
    for(int i=n;i>=1;i--) {
        mnxt[i]=max(mnxt[i+1],nxt[i]);
    }
}

void work() {
    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);
    }
} 

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]) {
            fro++;
        }
        else break;
    }
    
    if(fro==n/2) {
        printf("%d",n);
        return 0;
    }
    
    n-=fro*2;
    
    for(int i=1;i<=n;i++) {
        s[i]=s[i+fro];
    }
    
    Manacher(); Kmp(); work();
    reverse(s+1,s+1+n);
    Manacher(); Kmp(); work();
    
    printf("%d",ans+fro*2);
    
    return 0;
} 
View Code

 

posted @ 2023-07-29 10:52  Trmp  阅读(12)  评论(0编辑  收藏  举报
-->