20210820考试

20210820

eriri

先去 \(i\)\(j\) 更加优秀,就需要满足:

\[(a_it+b_i+1)*a_j\leq(a_jt+b_j+1)*a_i \]

\[(b_i+1)*a_j\leq(b_j+1)*a_i \]

我们先对所有商店进行这样的排序,然后实际情况就在序列中。

当没有 \(a_i=0\) 的商店时,我们最多去 \(O(log T)\) 个商店,因此可以减少复杂度。

设共有 \(k\)\(a_i > 0\) 的商店,对这 \(k\) 个商店做算法三的 dp,对于每个最终状态\(dp(k, j)\) ,再贪心地按照 \(b_i\) 从小到大的顺序检查还能去几个 \(a_i = 0\) 的商店.

#include<bits/stdc++.h>
using namespace std;
#define int long long 
const int N=2e5+5;
struct node{
    int a,b;
}a[N];
bool cmp(node x,node y){
    return x.a*(y.b+1)>y.a*(x.b+1)||x.a*(y.b+1)==y.a*(x.b+1)&&x.b<y.b;
}
int n,m,T,f[N],b[N],ans;
signed main(){
    
    cin>>n>>T;
    for(int i=1;i<=n;i++) scanf("%lld%lld",&a[i].a,a[i].b);
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=40;i++) f[i]=T+1;//c
    for(int i=1;i<=n;i++){
        if(a[i].a) for(int j=40;j>=1;j--) f[j]=min(f[j],(f[j-1]-1)*(a[i].a+1)+a[i].b);
        else{
            for(int j=i;j<=n;j++) b[j-i+1]=a[i].b; 
            m=n-i+1;
            sort(b+1,b+m+1); break;
        }
    }
    for(int j=0;j<=40;j++)
        if(f[j]<=T) {
            int res=T-f[j],num=j;
            for(int i=1;i<=m;i++){
                if(res>=b[i]+1) num++,res-=b[i]+1;
                else break;
            }
            ans=max(ans,num);
        } else break;
    cout<<ans<<endl;
    system("pause");
    return 0;
}

yui

一眼容斥,转换成求先手必败的情况。
\(p(i) = (2n − 1)^i\) ,即 \(i\) 堆石子的总方案数。
\(f(i)\) 表示 \(i\) 堆石子时先手必败的方案数。
我们考虑让前 \(i − 1\) 堆石子任意取,通过调整最后一堆石子的数目使得异或和为\(0\) ,方案数为 \(p(i − 1)\)

若前 \(i-1\) 堆的之子异或和是0,因为最后一堆不能取到 \(0\) ,所以不合法的方案数为 \(f(i-1)\)

若前 \(i-1\) 堆的石子中,有 \(i-2\) 石子异或起来是 \(0\) 那么剩下两堆只能相同个数,方案数为 \((i-1)*(2^n-i+1)*f(i-1)\)

\[f(i)=p(i-1)-f(i-1)-(i-1)*(2^n-i+1)*f(i-1) \]

\(O(n)\) 递推即可。

#include<bits/stdc++.h>
using namespace std;
const int N=1e7+10,mod=1e9+7;
int n,f[N],ans,P[N],Pow;
int qmi(int a,int b){
    int res=1; while(b){if(b&1) res=1ll*res*a%mod; b>>=1; a=1ll*a*a%mod;} return res;
}
int main(){
    cin>>n;
    Pow=qmi(2,n);
    P[0]=1; f[0]=1;
    for(int i=1;i<=n;i++) P[i]=1ll*P[i-1]*(Pow-i)%mod;
    for(int i=2;i<=n;i++)
        f[i]=(P[i-1]-f[i-1]-1ll*f[i-1]*(i-1)%mod*(Pow-i+1)%mod)%mod;
    cout<<(P[n]-f[n])%mod<<endl;
    return 0;
}

nanami

左右搜索+状压 \(dp\) 搜索图中的最大团。

具体地,取 \(p = n/2\)
对前 \(p\) 个点,预处理 \(f(S)\) 表示点集 \(S\) 的导出子图的最大团大小。
对后 \(n − p\) 个点,枚举一个集合 \(T\) , 若点集 \(T\) 的导出子图是团,并且 \(T\) 中所有点邻居的交与前 \(p\) 个点交集为 \(S\) ,就找到了一个大小为 \(|T| + f(S)\) 的团。

时间复杂度为 \(O(2^{n/2}n)\)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n, m, x, f[(1 << 20) + 10], pre, ans;
ll g[50];
int main() {
    scanf("%d%d%d", &n, &m, &x);
    for(int i=1,x,y;i<=m;i++){scanf("%d%d",&x,&y);g[x]|=1ll<<(y-1);g[y]|=1ll<<(x-1);}
    pre=min(n,20);
    for(int S=1;S<(1<<pre);S++){
        for (int i=1;i<=pre;i++)
            if(S&(1<<(i-1))) {
                int T=g[i]&S;
                f[S]=max(f[S],f[T]+1);
            }
    }
    for (ll S=0;S<(1ll<<n);S+=1<<pre) {
        bool flag=true;
        int T=(1<<pre)-1,num=0;
        for (int i=pre+1;i<=n;i++)
            if (S&(1ll<<(i-1))){
                num++;
                if((S&g[i])!=(S-(1ll<<(i-1)))) flag=false;
                else  T&=g[i];
            }
        if(flag) ans=max(ans,num+f[T]);
    }
    printf("%.6lf", (double)x*x*(ans-1)/ans/2);
    return 0;
}

ichigo

第二类斯特林数

对于这个式子,我们可以转化成:

\[ans=(\sum w_i)*(\binom{n}{k}+(n-1)\binom{n-1}{k}) \]

我们利用容斥原理,计算第二类斯特林数:

\[\binom{n}{k}=\frac{1}{k!}\sum^k_{i=0}(-1)^i\binom{k}{i}(k-i)^n \]

线性筛处理积性函数 \(f(i)=i^n\) 即可满分。

#include<bits/stdc++.h>
using namespace std;
#define int long long 
const int N=1e6+5,mod=998244353;
int fac[N],suma,sumw,n,k;
int qmi(int a,int b){
    int res=1; while(b){if(b&1) res=1ll*res*a%mod; b>>=1; a=1ll*a*a%mod;} return res;
}
int C(int n,int m){return (fac[n]*qmi(fac[m],mod-2)%mod*qmi(fac[n-m],mod-2)%mod);}
int calc(int n,int k){
    int sum=0;
    for(int i=0;i<=k;i++){ 
        if(i&1) sum=((sum-C(k,i)*qmi(k-i,n))%mod+mod)%mod;
        else sum=(sum+C(k,i)*qmi(k-i,n)%mod)%mod;
    }
    return qmi(fac[k],mod-2)*sum%mod;
}
signed main(){
    cin>>n>>k; fac[0]=1;
    for(int i=1;i<=n;i++) fac[i]=(fac[i-1]*i)%mod; 
    suma=(calc(n,k)+calc(n-1,k)*(n-1)%mod)%mod;
    for(int i=1,w;i<=n;i++){
        scanf("%lld",&w); sumw+=w;
        if(sumw>=mod) sumw-=mod; 
    }
    cout<<suma*sumw%mod<<endl;
    return 0;
}
posted @ 2021-08-20 20:27  Evitagen  阅读(43)  评论(0编辑  收藏  举报