zoukankan      html  css  js  c++  java
  • NOIP 模拟 $15; ext{夜莺与玫瑰}$

    题解

    一道很妙的题,让求对于一个矩阵中,两点相连成线,有多少条直线,他们的交集是有限集。

    转化一下题目,发现水平和竖直的只有 (n+m) 条,而左斜和右斜的条数是相同的,所以我们只需求出左或右中的即可

    这个矩阵中一共有 (sum_{a=1}^{n-1}sum_{b=1}^{m-1}[gcd(a,b)=1]) 条斜率不同的直线,那么对于每一种斜率,又有 ((n-a)×(m-b)) 个点
    可以伸出来,但是会有重复的所以要减去 ((n-2×a)×(m-2×b))

    所以最后可以推出来一个总式子

    [sum_{a=1}^{n-1}sum_{b=1}^{m-1}[gcd(a,b)=1](n-a)×(m-b)-max(n-2×a,0)×max(m-2×b,0) ]

    (60pts) 到手

    考虑如何优化它,这里提供一种 (O(n)) 查询的做法。

    我们维护三个数组 (gcd) ( m gnm) ( m gsm)( m gnm_{i,j}) 表示从 (1~j) 有多少个和 (i) 互质的数,( m sum) 表示就是这些数的和。

    对于 ( m gcd) 我们可以 (n^2) 预处理

    查询时,我们枚举 (a),对于后边的式子,拆一下

    [(n-a)×m-(n-a)×b-(n-2×a)×m+(n-2×a)×2×b ]

    这个式子,根据我们维护的三个数组就可以求出来了。

    Code
    #include<bits/stdc++.h>
    #define ri register int
    #define p(i) ++i
    using namespace std;
    namespace IO{
        char buf[1<<21],*p1=buf,*p2=buf;
        #define gc() p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++
        template<typename T>inline void read(T &x) {
            ri f=1;x=0;register char ch=gc();
            while(ch<'0'||ch>'9') {if (ch=='-') f=0;ch=gc();}
            while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
            x=f?x:-x;
        }
    }
    using IO::read;
    namespace nanfeng{
        #define int unsigned int
        #define cmax(x,y) ((x)>(y)?(x):(y))
        #define cmin(x,y) ((x)>(y)?(y):(x))
        #define FI FILE *IN
        #define FO FILE *OUT
        typedef long long ll;
        static const int N=4e3+7,MOD=(1<<30)-1;
        int gsm[N][N],T,ans,n,m;
        short num[N][N],*gcd[N];
        inline void init() {
            ri n=N-7;
            // int *(gcd[N])=new int[n+7];
            gcd[0]=new short[n+7];
            for (ri i(1);i<=n;p(i)) {
                gcd[i]=new short[n+7];
                gcd[i][i]=gcd[i][0]=gcd[0][i]=i;
                // printf("i=%d
    ",i);
                for (ri j(1);j<i;p(j)) 
                    gcd[j][i]=gcd[i][j]=gcd[j][i%j];    
            }
            delete gcd[0];
            for (ri i(1);i<=n;p(i)) {
                for (ri j(1);j<=n;p(j)) {
                    if (gcd[i][j]==1) gsm[i][j]=j,num[i][j]=1;
                    gsm[i][j]+=gsm[i][j-1];
                    num[i][j]+=num[i][j-1];
                }
                delete gcd[i];
            }
        }
        inline int main() {
            // FI=freopen("nanfeng.in","r",stdin);
            // FO=freopen("nanfeng.out","w",stdout);
            init();
            read(T);
            for (ri z(1);z<=T;p(z)) {
                read(n),read(m);ans=0;
                ri bs=n*m;
                for (ri i(1);i<=n-1;p(i)) {
                    ans=(ans+(int)num[i][m-1]*(n-i)*m);
                    ans-=(n-i)*gsm[i][m-1];
                    if (i*2>=n) continue;
                    int lm=(m&1)?m/2:m/2-1;
                    ans-=(n-2*i)*(m*num[i][lm]-(gsm[i][lm]<<1));
                }
                printf("%u
    ",(ans*2+n+m)&MOD);
            } 
            return 0;
        }  
        #undef int
    }
    int main() {return nanfeng::main();} 
    

    小技巧,这个模数很特殊,可以自然溢出,减少低效的取模

  • 相关阅读:
    Java根据html模板创建 html文件
    java.lang.NumberFormatException: For input string:"filesId"
    使用java开源工具httpClient及jsoup抓取解析网页数据
    JBPM5流程设计器jbpm-designer-2.4.0.Final-tomcat.war的部署没法访问的问题
    MyEclipse8.0 注册码生成代码
    图片转为byte[]、String、图片之间的转换
    java中Xml、json之间的相互转换
    java二维码小试牛刀
    进度条脚本
    如何制作一寸、二寸、六寸照片。以后不用再去照相馆了!!! 转~版本更新
  • 原文地址:https://www.cnblogs.com/nanfeng-blog/p/15018879.html
Copyright © 2011-2022 走看看