1. 程式人生 > 其它 >多校衝刺 noip 10.29

多校衝刺 noip 10.29

多校衝刺 noip 10.29

是我眼瞎?然後導致我成功切掉最後一題改命了?

\(JKlover\)出的題還算是良心,好歹是給大樣例了

關於我第二類斯特林數不會這個事情導致我在第一題上剛了兩個小時,這個是嚴重策略失誤

這次看到每一個題都有那麼一點點的頭緒

但是思維深度較深,需要不斷地轉化題意

很好的一套題,鍛鍊思維和優化演算法的能力

但是暴力分只有爆搜,沒有啥可以拿得出手的暴力

T1 莓良心

這個我考場上的思路比較麻煩,我欽定第一個是誰,然後列舉後面的方案數

過程中乘上每一個集合裡的個數,這樣就算出來每一個數應該加多少遍

最後直接乘上權值總和就好了

SB_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=2e3+5;
const int mod=998244353;
int ksm(int x,int y){
    int ret=1;
    while(y){
        if(y&1)ret=ret*x%mod;
        x=x*x%mod;y>>=1;
    }return ret;
}
int n,k,dp[N][N],ans;
int w[N],sum;
int jc[N],inv[N];
int C(int x,int y){return jc[x]*inv[y]%mod*inv[x-y]%mod;}
signed main(){
    freopen("ichigo.in","r",stdin);
    freopen("ichigo.out","w",stdout);
    scanf("%lld%lld",&n,&k);
    fo(i,1,n)scanf("%lld",&w[i]),sum=(sum+w[i])%mod;
    jc[0]=1;fo(i,1,n)jc[i]=jc[i-1]*i%mod;
    inv[0]=1;inv[n]=ksm(jc[n],mod-2);
    fu(i,n-1,1)inv[i]=inv[i+1]*(i+1)%mod;
    dp[0][0]=1;
    fo(i,1,n)fo(j,1,min(n,k))dp[i][j]=(dp[i][j]+dp[i-1][j-1]+dp[i-1][j]*j%mod)%mod;
    fo(i,1,n-k+1)ans=(ans+i*C(n-1,i-1)%mod*dp[n-i][k-1])%mod;
    ans=ans*sum%mod;
    printf("%lld",ans);
    return 0;
}

我考場上寫的那個遞推(dp)好像就是第二類斯特林數的遞推式

但是如果用我考場山寫的那個式子,即使是用了第二類斯特林數的容斥求法也無法切掉

真正的思路是,對於兩個數,如果出現在同一集合裡,那麼就會加上他倆的權值

\[\begin{array}{l} \text { ans }=\left(\sum w_{i}\right) \cdot\left\{\begin{array}{l} n \\ k \end{array}\right\}+\sum_{u \neq v}\left(w_{u}+w_{v}\right) \cdot\left\{\begin{array}{c} n-1 \\ k \end{array}\right\} \\ \text { ans }=\left(\sum w_{i}\right) \cdot\left(\left\{\begin{array}{l} n \\ k \end{array}\right\}+(n-1)\left\{\begin{array}{c} n-1 \\ k \end{array}\right\}\right) \end{array} \]

這樣的話就用兩個斯特林數解決了,而這兩個斯特林數可以在\(\mathcal{O(nlogn)}\)

\(\mathcal{O(n)}\)的時間內得到

AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=1e6+5;
const int mod=998244353;
int ksm(int x,int y){
    int ret=1;
    while(y){
        if(y&1)ret=ret*x%mod;
        x=x*x%mod;y>>=1;
    }return ret;
}
int n,k,ans,res1,res2;
int p[N],cnt,pw[N];
bool vis[N];
int w[N],sum,mi[N];
int jc[N],inv[N];
int C(int x,int y){return jc[x]*inv[y]%mod*inv[x-y]%mod;}
signed main(){
    freopen("ichigo.in","r",stdin);
    freopen("ichigo.out","w",stdout);
    scanf("%lld%lld",&n,&k);
    fo(i,1,n)scanf("%lld",&w[i]),sum=(sum+w[i])%mod;
    jc[0]=1;fo(i,1,n)jc[i]=jc[i-1]*i%mod;
    inv[0]=1;inv[n]=ksm(jc[n],mod-2);
    fu(i,n-1,1)inv[i]=inv[i+1]*(i+1)%mod;
    pw[1]=1;fo(i,2,n){
        if(!vis[i])p[++cnt]=i,pw[i]=ksm(i,n-1);
        for(int j=1;j<=cnt&&i*p[j]<=n;j++){
            pw[i*p[j]]=pw[i]*pw[p[j]]%mod;
            vis[i*p[j]]=true;
            if(i%p[j]==0)break;
        }
    }
    int bas=1;fo(i,0,k){res2=(res2+C(k,i)*pw[k-i]%mod*bas+mod)%mod;bas=-bas;}res2=res2*inv[k]%mod;
    bas=1;fo(i,0,k){res1=(res1+C(k,i)*pw[k-i]%mod*(k-i)%mod*bas+mod)%mod;bas=-bas;}res1=res1*inv[k]%mod;
    ans=(res1+(n-1)*res2)%mod;
    ans=ans*sum%mod;
    printf("%lld",ans);
    return 0;
}

T2 盡梨了

說真的這個題我考場上一點點的思路都沒有

我雖然想到排序方式了,但是,排好之後,從前往後找,給的資料一個也過不了

然後我心態就崩了,直接去死好吧

後來考完了之後發現,排序方式是對的,只不過不能順著選

這個排序只是告訴你,我選好了商店集合,順序是這樣的

接下來就可以\(dp\)了,設\(dp_{i,j}\)表示前\(i\)個商店,去了\(j\)個的最小時間

直接轉移就好了,發現複雜度好像不太行啊

但是有一點,時間的增加是指數級的,所以\(j\)的上界是\(log\)

這樣複雜度就到了\(log\)級別,但是你發現指數級別是對於\(a\)不等於\(0\)的情況來說的

所以最後面的\(0\)就按照\(b\)從大到小排序,能加多少加多少就行了

AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=2e5+5;
const int inf=0x3f3f3f3f3f3f3f3f;
int n,T;
struct node {
    int a,b;
    node(){}
    bool operator < (node x)const{
        return x.a*(b+1)<a*(x.b+1);
    }
}sca[N];
int ans,now=1;
int f[N][35];
bool com(node x,node y){return x.b<y.b;}
signed main(){
    freopen("eriri.in","r",stdin);
    freopen("eriri.out","w",stdout);
    scanf("%lld%lld",&n,&T);
    fo(i,1,n)scanf("%lld%lld",&sca[i].a,&sca[i].b);
    sort(sca+1,sca+n+1);
    memset(f,0x3f,sizeof(f));
    int pos=n+1;
    fo(i,0,n)f[i][0]=0;
    fo(i,1,n){
        if(!sca[i].a){pos=i;break;}
        fo(j,1,min(i,30ll))f[i][j]=f[i-1][j];
        fo(j,1,min(i,30ll)){
            if(f[i-1][j-1]==inf)continue;
            if(f[i-1][j-1]+1+(f[i-1][j-1]+1)*sca[i].a+sca[i].b<=T)
                f[i][j]=min(f[i][j],f[i-1][j-1]+1+(f[i-1][j-1]+1)*sca[i].a+sca[i].b);
        }
    }
    fo(i,0,min(pos-1,30ll))if(f[pos-1][i]<=T)ans=max(ans,i);
    int sum=0;
    sort(sca+pos,sca+n+1,com);
    fo(i,pos,n){
        sum+=sca[i].b+1;
        fo(j,1,min(pos-1,30ll)){
            if(sum+f[pos-1][j]<=T)ans=max(ans,j+i-pos+1);//cout<<j<<" "<<i<<" "<<pos<<" "<<j+i-pos+1<<endl;
        }
    }
    printf("%lld",ans);
}

T3 團不過

看這個題的時候只剩下\(10min\)

然後就搜了一下就走了,其實這個題已經非常的善良了

遞推就能解決,完全不是我們想的那麼難

請看題解......我咕了

AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=1e7+5;
const int mod=1e9+7;
int ksm(int x,int y){
    int ret=1;
    while(y){
        if(y&1)ret=ret*x%mod;
        x=x*x%mod;y>>=1;
    }return ret;
}
int n,ans;
int pw[N],p[N];
int f[N];
signed main(){
    freopen("yui.in","r",stdin);
    freopen("yui.out","w",stdout);
    scanf("%lld",&n);
    pw[0]=1;fo(i,1,n)pw[i]=pw[i-1]*2%mod;
    p[0]=1;fo(i,1,n)p[i]=p[i-1]*((pw[n]-i+mod)%mod)%mod;
    f[1]=f[2]=0;
    fo(i,3,n)f[i]=(p[i-1]-f[i-1]-(i-1)*(pw[n]-i+1)%mod*f[i-2]%mod+mod+mod)%mod;
    printf("%lld",(p[n]-f[n]+mod)%mod);
    return 0;
}

T4 七負我

這次能在考場上切掉這個題,完全依賴我手殘劃過了把這個當成了第二題

然後認為很簡單,就非常自信的\(meet\ in\ the\ middle\)

結論就是,完全圖的貢獻最大

所以我們只需要找到這張圖中的最大完全子圖就好了

先找前一半,再找後一半,直接合並!!!!!

AC_code
#include<bits/stdc++.h>
using namespace std;
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=41;
int n,m,l,r,ul,ur;
int e[N][2],mx;
int sum[1<<20],ys[1<<20],rit[1<<20];
bool vis[1<<20];
double x,ans;
signed main(){
    freopen("nanami.in","r",stdin);
    freopen("nanami.out","w",stdout);
    scanf("%d%d%lf",&n,&m,&x);
    l=n>>1;ul=(1<<l)-1;
    r=n-l;ur=(1<<r)-1;
    fo(i,1,m){
        int x,y;
        scanf("%d%d",&x,&y);
        if(y>l)e[x][1]|=(1<<y-l-1);
        else e[x][0]|=(1<<y-1);
        if(x>l)e[y][1]|=(1<<x-l-1);
        else e[y][0]|=(1<<x-1);
    }
    fo(i,1,l)e[i][0]|=(1<<i-1);
    fo(i,1,r)e[i+l][1]|=(1<<i-1);
    fo(i,1,max(ul,ur))sum[i]=sum[i-(i&-i)]+1;
    fo(s,1,ul){
        int now,num=sum[s],rig=ur;
        bool flag=true;
        fo(i,1,l){
            if(!((s>>i-1)&1))continue;
            now=e[i][0]&s;rig&=e[i][1];
            if(sum[now]!=num){flag=false;break;}
        }
        if(flag){
            mx=max(mx,num);
            ys[s]=rig;
        }
    }
    fo(s,1,ur){
        int now,num=sum[s];
        bool flag=true;
        fo(i,1,r){
            if(!((s>>i-1)&1))continue;
            now=e[i+l][1]&s;
            if(sum[now]!=num){flag=false;break;}
        }
        if(flag){
            mx=max(mx,num);
            vis[s]=true;
        }
    }
    memset(rit,-1,sizeof(rit));
    fu(s,ul,1){
        if(!ys[s]||sum[s]+sum[ys[s]]<mx)continue;
        if(rit[ys[s]]!=-1){mx=max(mx,sum[s]+rit[ys[s]]);continue;}
        int now=0;
        for(int t=ys[s];t;t=(t-1)&ys[s]){
            if(vis[t])now=max(now,sum[t]);
        }
        rit[ys[s]]=now;
        mx=max(mx,sum[s]+now);
    }
    ans=1.0*mx*(mx-1)/2.0;
    ans=ans*(1.0/(1.0*mx*mx))*x*x;
    printf("%.6lf",ans);
    return 0;
}