• SDU暑期集训排位(1)题解


    D - Dicy Numbers

    积性函数、组合数。其实如果数据组数比较少的话,我们很容易yy到一个矩阵快速幂的做法,复杂度为$O(div(N)^3logM) $。数据组数这么多,$M$又这么大,那我们可以猜想下一定含有什么规律,打个表就可以发现,如果固定$M$,$S(N,M)$是关于$N$的积性函数,那么问题就简单很多了,接下来,我们只要求$S(p^c,M)$,其中$p$是质数。这个东西,其实等于$C_{c+M-1}^c$,为什么呢?首先,我们设第$i$个质数的次幂$a_i$,那么,$S(N,M)$其实就等价于求$prod_{1}^{M}(1+a_i)=N$的方案数,而现在$N=p^c$,那么对于任意的$i$,都有$a_i=p^{x_i}-1$,其中$0leq x_i leq c$,那也就是说等价于求$sum_{i=1}^{M}x_i=c$的方案数,显然答案就是$C_{c+M-1}^c$。对于组合数的计算,注意到虽然$M$很大,但是$c$会很小,由于$C_n^m=frac{n!}{m!(n-m)!}=frac{prod_{j=0}^{m-1}(n-j)}{m!}$,所以可以暴力计算分子分母,然后对分母求逆元即可。最后,为了求出答案,我们只要将$N$进行质因数分解,求出每个质数的次幂,并计算出对应的方案数,最后乘起来就是答案了。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<vector>
     4 const int N=2e6+1;
     5 std::vector<int> g[N];
     6 bool vis[N];
     7 typedef long long ll;
     8 const ll mod=1e9+7;
     9 int f[N];
    10 ll qpow(ll x,ll y) {
    11     ll res=1;
    12     for(;y;y>>=1,x=x*x%mod) if(y&1) res=res*x%mod;
    13     return res;
    14 }
    15 ll C(int n,int m) {
    16     if(n<m) return 0;
    17     ll up=1,down=1;
    18     for(int j=0;j<m;j++) up=up*(n-j)%mod,down=down*(j+1)%mod;
    19     return up*qpow(down,mod-2)%mod;
    20 }
    21 int main() {
    22     for(int i=1;i<N;i++) f[i]=i;
    23     for(int i=2;i<N;i++) if(!vis[i]) {
    24         for(int j=i;j<N;j+=i) {
    25             int num=0;
    26             while(f[j]%i==0) num++,f[j]/=i;
    27             g[j].push_back(num);
    28             vis[j]=1;
    29         }
    30     }
    31     int T;
    32     scanf("%d",&T);
    33     while(T--) {
    34         int n,m;
    35         scanf("%d%d",&n,&m);
    36         ll ans=1;
    37         for(int v:g[n]) ans=ans*C(m+v-1,v)%mod;
    38         printf("%lld
    ",ans);
    39     }
    40     return 0;
    41 }
    View Code

    E - Everyone wants Khaleesi

    考虑A先走一步到达点x,首先x要是必胜点,x是必胜点的条件是:如果x不是n的话,那么x出度的点必须有两个必胜点,否则B一定可以割掉通往必胜点的路径。n点是必胜点,从n点往前推,会发现所有点都是必败点,因为是dag。所以除非A可以一步到达点n,否则A必败。

    F - Flipping Rectangles

    考虑将r1移到和r2有面积交的地方需要多少步,和k比较大小,接下来就是枚举上下方向移动一步,左右方向移动一步即可

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<stack>
    #include<queue>
    using namespace std;
    typedef long long ll;
    int T;
    ll k;
    ll l1, l2, r1, r2, u1, u2, d1,d2;
    ll x, y, x1, y1;
    ll cac(ll a, ll b, ll c){
        if(a<=b) return 0;
        return (a-b+c-1)/c;
    }
    ll cac1(ll l1, ll r1, ll u1, ll d1){
        ll u=min(u1,u2); ll d=max(d1,d2);
        ll l=max(l1,l2); ll r=min(r1,r2);
        if(u<=d||l>=r) return 0;
        return (u-d)*(r-l);
    }
    int main(){
        scanf("%d",&T);
        while(T--){
            ll p, q;
            scanf("%lld%lld%lld%lld",&p,&q,&x,&y);
            l1=p; r1=p+x; d1=q; u1=q+y;
            scanf("%lld%lld%lld%lld",&p,&q,&x1,&y1);
            l2=p; r2=p+x1; d2=q; u2=q+y1;
            scanf("%lld",&k);
            ll rs=cac(l2,r1,x); ll ls=cac(l1,r2,x); ll us=cac(d2,u1,y);  ll ds=cac(d1,u2,y);
            ll sum=rs+ls+us+ds;
            if(sum>k){
                printf("0
    "); continue;
            }
            k-=sum; ll ans=0;
            if(rs) l1+=rs*x, r1+=rs*x;  if(ls) l1-=ls*x, r1-=ls*x;
            if(us) u1+=us*y, d1+=us*y;  if(ds) u1-=ds*y, d1-=ds*y;
            ans=max(ans,cac1(l1,r1,u1,d1));
            k--;
            if(k>=0){
                ans=max(ans,cac1(l1,r1,u1+y,d1+y)), ans=max(ans,cac1(l1,r1,u1-y,d1-y));
                ans=max(ans,cac1(l1+x,r1+x,u1,d1)), ans=max(ans,cac1(l1-x,r1-x,u1,d1));
            }
            k--;
            if(k>=0){
                ans=max(ans,cac1(l1+x,r1+x,u1+y,d1+y)), ans=max(ans,cac1(l1+x,r1+x,u1-y,d1-y));
                ans=max(ans,cac1(l1-x,r1-x,u1+y,d1+y)), ans=max(ans,cac1(l1-x,r1-x,u1-y,d1-y));
            }
            printf("%lld
    ",ans);
        }
        return 0;
    }
    View Code

    G - Gift Pack

    数位$ dp $。这个题比较容易想贪心,譬如说我当时在比赛过程中,想贪心的从高位开始取,但是很容易发现,如果枚举该位的$8$种情况,并非只会产生$0$或者$1$,也可能产生$2$,也就是说,会产生进位,这样贪心就不对了。正确的做法是,我们可以数位$dp$,用$dp[i][al][ar][b][c]$表示从最高位到第i位的贴边界的情况为$(al,ar,b,c)$时的最大答案,其中$(al,ar,b,c)$分别指第一个数是否贴下界,第一个数是否贴上界,第二个数是否贴上界以及第三个数是否贴上界,转移的话,只要枚举该位的$8$种情况,判断下是否越界,将所有情况取最大即可。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 typedef long long ll;
     5 typedef std::pair<ll,ll> P;
     6 ll dp[66][2][2][2][2];
     7 ll L,R,A,B;
     8 ll dfs(int pos,bool al,bool ar,bool b,bool c) {
     9     if(pos<0) return 0;
    10     if(dp[pos][al][ar][b][c]!=-1) return dp[pos][al][ar][b][c];
    11     int aL=0,aR=1,upb=1,upc=1;
    12     if(al) aL=L>>pos&1;
    13     if(ar) aR=R>>pos&1;
    14     if(b) upb=A>>pos&1;
    15     if(c) upc=B>>pos&1;
    16     ll &ans=dp[pos][al][ar][b][c];
    17     for(int i=0;i<8;i++) {
    18         int na,nb,nc;
    19         na=i&1;
    20         nb=i>>1&1;
    21         nc=i>>2&1;
    22         if(na<aL||na>aR) continue;
    23         if(nb>upb) continue;
    24         if(nc>upc) continue;
    25         ll tmp=(na^nb)+(na^nc)+(nb&nc);
    26         tmp<<=pos;
    27         ans=std::max(ans,tmp+dfs(pos-1,al&&na==aL,ar&&na==aR,b&&nb==upb,c&&nc==upc));
    28     }
    29     return ans;
    30 }
    31 int main() {
    32     int n;
    33     //printf("%lld
    ",1LL<<61);
    34     scanf("%d",&n);
    35     while(n--) {
    36         scanf("%lld%lld%lld%lld",&L,&R,&A,&B);
    37         memset(dp,-1,sizeof(dp));
    38         printf("%lld
    ",dfs(61,1,1,1,1));
    39     }
    40     return 0;
    41 }
    View Code

     楼上代码不好看

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    int n;
    ll l, r, a, b, dp[70][2][2][2][2];
    int ua[70], ub[70], dl[70], dr[70];
    ll dfs(int k, bool w, bool x, bool y, bool z){
        if(k==-1) return 0;
        if(dp[k][w][x][y][z]!=-1) return dp[k][w][x][y][z];
        int upa=y?ua[k]:1; int upb=z?ub[k]:1; int upl=w?dl[k]:0; int upr=x?dr[k]:1;
        ll ans=0;
        for(int i=0;i<=upa;i++)for(int j=0;j<=upb;j++) for(int p=upl;p<=upr;p++){
            ll g=((p^i)+(i&j)+(j^p))*(1LL<<k);
            ans=max(ans,dfs(k-1,w&&(p==upl),x&&(p==upr),y&&(i==upa),z&&(j==upb))+g);
        }
        return dp[k][w][x][y][z]=ans;
    }
    int T;
    void cac(int *a, ll x){
        for(int i=0;i<70;i++) a[i]=0;
        int k=0;
        while(x){
           a[k++]=(x&1); x>>=1;
        }
    }
    int main(){
        scanf("%d",&T);
        while(T--){
            memset(dp,-1,sizeof(dp));
            scanf("%lld%lld%lld%lld",&l,&r,&a,&b); cac(ua,a); cac(ub,b); cac(dl,l); cac(dr,r);
            printf("%lld
    ",dfs(61,1,1,1,1));
        }
        return 0;
    }
    View Code
  • 相关阅读:
    人生,别认输,因为没人希望你赢
    一张图看懂开源许可协议,开源许可证GPL、BSD、MIT、Mozilla、Apache和LGPL的区别
    Android Studio 出现 Gradle's dependency cache may be corrupt 解决方案
    清华梦的粉碎——转自王垠
    label smooth
    <现代C++实战30讲>笔记 01 | 堆、栈、RAII:C++里该如何管理资源?
    h5转pb的两个坑
    opencv库的像素x,y,width,height,col,row的对应关系
    detect 导图
    keras多gpu训练
  • 原文地址:https://www.cnblogs.com/Onlymyheart/p/11233547.html
Copyright © 2020-2023  润新知