多校冲刺 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;
}
posted @ 2021-10-29 16:59  fengwu2005  阅读(58)  评论(1编辑  收藏  举报