20240805題解

junjunccc發表於2024-08-07

考試時生病了捏

括號 (bracket)

有一個由四種字元()[]組成的括號序列,問最少加入多少個字元後,能變成合法的括號序列。
題解:區間DP,\(f(l,r)\)表示第\(l\)個字元到第\(r\)個字元之間最少需要加入多少個。
求具體方案就找到\(f(l,r)\)是從哪裡轉移來的即可。
程式碼:

#include<cstdio>
#include<cstring>
const int N=105,INF=0x3f3f3f3f;
int n,f[N][N],len;
char s[N],t[N<<1];
inline void Min(int&x,int y){y<x&&(x=y);}
void dfs(int l,int r){
    if(l>r)return;
    if(l==r){
        if(s[l]=='('||s[l]==')')t[++len]='(',t[++len]=')';
        if(s[l]=='['||s[l]==']')t[++len]='[',t[++len]=']';
        return;
    }
    if(((s[l]=='('&&s[r]==')')||(s[l]=='['&&s[r]==']'))&&f[l+1][r-1]==f[l][r]){
        t[++len]=s[l],dfs(l+1,r-1),t[++len]=s[r];
        return;
    }
    for(int i=l;i<r;i++)if(f[l][i]+f[i+1][r]==f[l][r]){
        dfs(l,i),dfs(i+1,r);
        return;
    }
}
int main(){
    freopen("bracket.in","r",stdin),freopen("bracket.out","w",stdout),scanf("%s",s+1),n=strlen(s+1);
    for(int i=1;i<=n;i++){
        f[i][i]=1;
        for(int j=i+1;j<=n;j++)f[i][j]=INF;
    }
    for(int len=2;len<=n;len++)for(int l=1,r=len;r<=n;l++,r++){
        if((s[l]=='('&&s[r]==')')||(s[l]=='['&&s[r]==']'))Min(f[l][r],f[l+1][r-1]);
        for(int k=l;k<r;k++)Min(f[l][r],f[l][k]+f[k+1][r]);
    }
    return dfs(1,n),puts(t+1),fflush(stdout),fclose(stdin),fclose(stdout),0;
}

切環 (ring)

題面:有一個大小為\(n\)個環,每個點上有數\(a_i\),將環切\(k\)次,每一段的和為\(s_1,\dots,s_{k}\),分數為\(s\)\(\gcd\),所有方案的分數之積為\(f_k\),對於\(0\le k\le n\),求\(f_k\bmod(10^9+7)\)
題解:對於\(s\)\(\gcd\)一定是所有\(a\)的和的\(gcd\)的因數。
\(a\)的和質因數分解,求每個質數在答案中的出現次數。
先列舉\(k\)
令列舉到的質數為\(p\)\(a\)的字首和\(\bmod p\)相等的情況下這一段才是\(p\)的倍數,設\(g_i\)表示\(a\)的字首和\(\bmod p=i\),那麼使\(p\)的出現次數就增加了\(cnt_i\choose k\)
由於貢獻次數在指數上,所以要\(\bmod(10^9+6)\),可以拆分成\(2\times(5*10^8+3)\)
程式碼:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cassert>
#define int long long
const int N=100005,mod1=1000000007,mod2=1000000006;
int n,a[N],s[N],fac[20],fc,cnt[20],ans[N],res[N],t[N],pow2[N],inv[N];
bool flag;
inline int _inv(int x){
	int ret=1,y=500000001;
	for(;y;x=x*x%mod2,y>>=1)if(y&1)ret=ret*x%mod2;
	return ret;
}
inline void prework(){
    pow2[0]=1,inv[1]=1;
    for(int i=1;i<=99994;i++)pow2[i]=pow2[i-1]*2%mod2;
    for(int i=2;i<=n;i++)if(i&1)inv[i]=_inv(i);
}
inline void factor(int x){
    for(int i=2;i*i<=x;i++)if(x%i==0){
        fac[++fc]=i;
        for(;x%i==0;)cnt[fc]++,x/=i;
    }
    if(x>1)fac[++fc]=x,cnt[fc]=1;
}
inline int pow(int x,int y){
    int ret=1;
    for(x%=mod1;y;x=x*x%mod1,y>>=1)if(y&1)ret=ret*x%mod1;
    return ret;
}
struct Var{
    int v,cnt;
    Var(){v=1,cnt=0;}
    inline void mul(int x){
        int t=__builtin_ctzll(x);
        cnt+=t,v=v*(x>>t)%mod2;
    }
    inline void div(int x){
        int t=__builtin_ctzll(x);
        cnt-=t,v=v*inv[x>>t]%mod2;
    }
    inline int val(){
        return v*pow2[cnt]%mod2;
    }
};
void add(int n){
    Var x;
    for(int i=1;i<=n;i++)x.mul(n-i+1),x.div(i),res[i]=(res[i]+x.val())%mod2;
}
signed main(){
    scanf("%lld",&n),prework();
    for(int i=1;i<=n;i++)scanf("%lld",a+i),s[i]=s[i-1]+a[i];
    factor(s[n]),ans[0]=s[n]%mod1;
    for(int i=1;i<=n;i++)ans[i]=1;
    for(int i=1;i<=fc;i++){
        int v=1;
        for(int i=1;i<=n;i++)res[i]=0;
        for(;cnt[i]--;){
            v*=fac[i];
            for(int i=1;i<=n;i++)t[i]=s[i]%v;
            std::sort(t+1,t+n+1);
            for(int i=1;i<=n;i++){
                int j=i;
                for(;j<n&&t[j+1]==t[j];j++);
                add(j-i+1),i=j;
            }
        }
        for(int j=1;j<=n;j++)ans[j]=ans[j]*pow(fac[i],res[j])%mod1;
    }
    for(int i=0;i<=n;i++)printf("%lld ",ans[i]);
    return puts(""),0;
}