zoukankan      html  css  js  c++  java
  • ysj的模拟赛

    链接:https://www.luogu.org/problemnew/show/P4917

    天守阁的地板

    题目描述

    为了使万宝槌能发挥出全部魔力,小碗会将买来的地板铺满一个任意边长的正方形(地板有图案,因此不允许旋转,当然,地板不允许重叠)来达到最大共鸣

    现在,她能够买到规格为a∗ba*bab的地板,为了省钱,她会购买尽可能数量少的地板

    现在,她想知道对于每一对a,b(1≤a,b≤n)a,b(1≤a,b≤n)a,b(1a,bn),她最少需要购买的地板数量

    由于输出可能很大,所以你只需要输出所有答案的乘积即可,为了避免高精度,小碗很良心的让你将答案对192608171926081719260817取模

    输入输出格式

    输入格式:

    第一行一个整数TTT,表示数据组数
    下面TTT行,每行一个整数nnn

    输出格式:

    TTT行,每行一个整数,表示取模后的答案

    输入输出样例

    输入样例#1: 复制
    4
    1
    2
    3
    100
    输出样例#1: 复制
    1
    4
    1296
    18996121

    说明

    样例解释:
    对于n=1,a,b仅有(1,1)(1,1)(1,1一种情况,只需要一块1∗1的地板,答案为1

    对于n=2,a,b(1,1),(1,2),(2,1),(2,2)四种情况,分别需要一块(1∗1)两块(1∗2),两块(2∗1),一块(2∗2)的地板,答案为1∗2∗2∗1=41*2*2*1=41221=4

    追加解释:a,b有四种取值,分别是(1,1),(1,2),(2,1),(2,2)

    当只能买到1∗1的地板时,需要一块(本身就是正方形)
    当只能买到1∗2的地板时,需要两块(两块拼在一起组成2∗2的正方形)
    当只能买到2∗1的地板时,需要两块(两块拼在一起组成2∗2的正方形)
    当只能买到2∗2的地板时,需要一块(本身就是正方形)

    答案就是这些数的乘积,即444

    T<=100, n <=1e6

    题解:一道非常好的数论题

    主要涉及了两个知识点(都做过,但并没有想到结合):

    第一个,求gcd(x, y) = d, x,y <= n 的对数,详见:https://www.cnblogs.com/EdSheeran/p/9329517.html

    第二个,乘除分块,详见:https://www.cnblogs.com/EdSheeran/p/9470009.html

    #include<bits/stdc++.h>
    using namespace std;
    #define LL long long
    const int M = 1e6 + 5;
    const LL mod = 19260817;
    LL sum[M],fac[M];
    int vfac[mod + 1]; 
    bool vis[M]; 
    int phi[M],  tot, prime[M];
    LL ksm(LL a, LL b){
        LL ret = 1;
        for(;b;b>>=1,a=a*a%mod)
            if(b&1)ret=ret*a%mod;
        return ret;
    }
    
    
    void pre(){
        fac[0] = vfac[0] = vfac[1] = 1;
        
        for(int i = 1; i < M; i++)
            fac[i] = fac[i - 1] * i % mod;
        for(int i = 2; i < mod; i++)
            vfac[i] = (-(LL)(mod/i) * vfac[mod%i] % mod + mod) % mod;
        
        phi[1] = 1;
        for(int i = 2; i < M; i++){
            if(!vis[i]) phi[i] = i - 1, prime[++tot] = i;
            for(int j = 1; j <= tot && (LL)i * prime[j] < M; j++){
                int m = i * prime[j];
                vis[m] = 1;
                if(i % prime[j] == 0){// if n%i == 0 phi[n*i] = phi[n] * i;
                    phi[m] = phi[i] * prime[j];
                    break;
                }
                phi[m] = phi[i] * phi[prime[j]];
            }
        }
        for(int i = 1; i < M; i++)sum[i] = sum[i - 1] + phi[i];
    }
    
    
    
    int main(){
        int T;
        scanf("%d", &T);
        pre();
        while(T--){
            int n;
            scanf("%d", &n);
            LL ans1 = ksm(fac[n], 2*n);
            LL ans2 = 1;
            for(int i = 1, rg; i <= n;){
                rg = n/(n/i);
                LL p = sum[n/i];
                ans2 = ans2* ksm(fac[rg]*(LL)vfac[fac[i-1]]%mod, 2*p-1) % mod;
                i = rg + 1;
            }
            ans2=ans2*ans2%mod;
            ans1 = ans1 * (LL)vfac[ans2]%mod;
            printf("%lld
    ", ans1);
        }
    }
    View Code

    4918 信仰收集

    输入样例#1: 复制
    3 7 8
    1 2
    3 2
    2 2
    4 3
    6 4
    1 2
    2 4
    4 5
    2 6
    7 6
    6 4
    3 2
    3 4
    输出样例#1: 复制
    2

     

    题解:dp[u][j]表示当前在u号节点,这段瞬移还有j才结束的最大收益;

    虽然dp方程我想出来了,但是实际操作我遇到了问题;

    一是怎么确定遍历顺序,我做了一遍SPFA,从距离大的倒着更新;(这应该是有问题的,而且常数大)

    二是我瞬移的花费与收获在出发时还是结束时计算;

    看了std,第一个问题可以用拓扑排序解决,通过入度更新,这样是显然正确的;

    第二个是在出发时减去花费,到达时加上当前点的贡献,不重不漏;

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int M = 2e5 + 10, ME = 3e5 + 10, inf = -2e9;
    
    int dp[M][52], d[2], w[2], here[M], deg[M];
    queue <int> q;
    struct edge{int v, nxt;}G[ME];
    int tot,cnt,dis[M],hh[M],h[M],a[M];
    bool inq[M];
    void add(int u, int v){
        G[++tot].v=v,G[tot].nxt=h[u],h[u]=tot;
    }
    
    int read(){
        int x=0,f=1;char c=getchar();
        while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
        return x*=f;
    }
    #define ex(i, u) for(int i = h[u]; i; i = G[i].nxt)
    void del(int u){
        ex(i, u){
            int v = G[i].v;
            deg[v]--;
            if(!deg[v] && v!=1)q.push(v);
        }
    }
    
    int main(){
        int u, v, n, m, k;
        scanf("%d%d%d", &n, &m, &k);
        scanf("%d%d%d%d", &d[0], &d[1], &w[0], &w[1]);
        for(int i = 1; i <= n; i++){
            u = read(), v = read();
            here[u] += v;
        }
        for(int i = 1; i <= k; i++){
            u = read(), v = read();
            add(u, v); deg[v]++; 
        }
        
        for(int i = 2; i <= m; i++)
            if(!deg[i])q.push(i);
        while(!q.empty()){
            int u=q.front();q.pop();
            del(u);
        }// the useless vertex
        q.push(1);
        memset(dp, 0x8f, sizeof(dp));
        dp[1][0] = here[1];
        while(!q.empty()){
            int u=q.front();q.pop();
            del(u);
            ex(i, u){
                int v=G[i].v;
                if(dp[u][0] > inf){
                    if(d[0]!=1) dp[v][d[0]-1] = max(dp[v][d[0]-1], dp[u][0] - w[0]);
                    else dp[v][0] = max(dp[v][0], dp[u][0] - w[0] + here[v]);
                    if(d[1]!=1)  dp[v][d[1]-1] = max(dp[v][d[1]-1], dp[u][0] - w[1]);
                    else dp[v][0] = max(dp[v][0], dp[u][0] - w[1] + here[v]);
                    
                }        
                if(dp[u][1] > inf)dp[v][0] = max(dp[v][0], dp[u][1] + here[v]);
                for(int j=1; j<d[1]; j++){
                    dp[v][j] = max(dp[v][j], dp[u][j+1]);
                }
            }
        }
        int ans = 0;
        for(int i = 1; i <= m; i++) ans = max(ans, dp[i][0]);
        printf("%d
    ", ans);
        
    }
    View Code
  • 相关阅读:
    C/C++多文件之间的变量定义
    PKU POJ 2186 Popular Cows 强连通分量
    重载函数
    ZOJ 2763 Prison Break
    201357 训练赛总结
    hdu 4467 Graph 构造
    201356 训练赛总结
    201353 NEERC 2012, Eastern subregional contest
    2013512 CF 183 总结
    一道动态规划
  • 原文地址:https://www.cnblogs.com/EdSheeran/p/9757297.html
Copyright © 2011-2022 走看看