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$,其中$0\leq 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\n",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\n"); 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\n",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\n",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\n",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\n",dfs(61,1,1,1,1));
    }
    return 0;
}
View Code

 

posted on 2019-07-23 19:23  唯我心  阅读(226)  评论(0编辑  收藏  举报