19_05_21校内训练[边上的整点]

题意

有一个n条横线m条竖线的正方形网格,求出(三点在整点上的)三角形的边上整点个数的期望。要求线性。


思考

ans=所有三角形的边经过的整点总个数÷所有三角形的整点个数。方便起见,默认三角形的三条边是有顺序但没方向的。

记cnt(i,j)表示平面上第i个点与第j个点连线经过了几个整点,范围是(    ],易证cnt(i,j)=gcd(|xi-xj|,|yi-yj|)。

1.三角形个数:
先枚举一条边,再看有多少个点能与它围成三角形。即

第一项表示所有的三个点组合,第二项表示去掉了退化成一条边的三角形,第三项表示去掉了退化成一个点的三角形。

2.整点个数:

还是枚举一条边,再看有多少个点能与它围成三角形。即

中间的减一表示把剩下的一个短点也扣掉,nm与它的差就是不在这个线段上的点的个数,并且每个有这条边的三角形贡献了cnt(p,q)。

但有不合法即退化成一条边的三角形。减去:

这个式子枚举了一个退化成一条边的三角形。中间的2表示决定了扣去的短点,那么剩下的部分只能从另一个端点开始枚举。此处强烈建议自己画图理解。

化简,得:

两式相减,得:

3.剩下的就是求解cnt和cnt2的事了。


考虑枚举|xp-xq|和|yp-yq|,则:

由于x=0和y=0能做到线性,只考虑x和y从1开始,则:

第一项是phi函数,第二项和第三项可以等差数列求和。

cnt平方也可以类似做。最后化为线性筛。


代码

 1 #pragma GCC oprimize 2
 2 #include<bits/stdc++.h>
 3 #define mod 1000000007
 4 #define G 500000004
 5 using namespace std;
 6 typedef long long int ll;
 7 const ll maxn=5E6+5;
 8 ll n,m;
 9 ll ans1,ans2,ans,tot;
10 ll prime[maxn],size,phi[maxn],g[maxn];
11 bool vis[maxn];
12 inline void add(ll&x,ll y)
13 {
14     x=(x+y)%mod;
15 }
16 ll gcd(int x,int y)
17 {
18     if(x==0)
19         return y;
20     if(y==0)
21         return x;
22     return x%y==0?y:gcd(y,x%y);
23 }
24 void init()
25 {
26     phi[1]=g[1]=1;
27     for(ll i=2;i<=n;++i)
28     {
29         if(!vis[i])
30         {
31             prime[++size]=i;
32             phi[i]=i-1;
33             g[i]=(i*i-1)%mod;
34         }
35         for(int j=1;j<=size&&prime[j]*i<=n;++j)
36         {
37             vis[prime[j]*i]=1;
38             if(i%prime[j]==0)
39             {
40                 phi[prime[j]*i]=phi[i]*prime[j];
41                 g[prime[j]*i]=g[i]*prime[j]%mod*prime[j]%mod;
42                 break;
43             }
44             phi[prime[j]*i]=phi[i]*(prime[j]-1);
45             g[prime[j]*i]=g[i]*(prime[j]*prime[j]%mod-1)%mod;
46         }
47     }
48 }
49 inline ll get(ll n,ll d)
50 {
51     ll m=(n-1)/d;
52     return (n*m%mod-d*m%mod*(m+1)%mod*G%mod+mod)%mod;
53 }
54 void solve1()
55 {
56     for(int i=1;i<=min(n-1,m-1);++i)
57         add(ans1,phi[i]*get(n,i)%mod*get(m,i)%mod*4);
58     for(ll x=0;x<=n-1;++x)
59         add(ans1,gcd(x,0)*(n-x)%mod*m%mod*2);
60     for(ll y=0;y<=m-1;++y)
61         add(ans1,gcd(y,0)*(m-y)%mod*n%mod*2);
62 }
63 void solve2()
64 {
65     for(int i=1;i<=min(n-1,m-1);++i)
66         add(ans2,g[i]*get(n,i)%mod*get(m,i)%mod*4);
67     for(ll x=0;x<=n-1;++x)
68         add(ans2,gcd(x,0)*gcd(x,0)%mod*(n-x)%mod*m%mod*2);
69     for(ll y=0;y<=m-1;++y)
70         add(ans2,gcd(y,0)*gcd(y,0)%mod*(m-y)%mod*n%mod*2);
71 }
72 ll qpow(ll x,ll y)
73 {
74     ll ans=1,base=x;
75     while(y)
76     {
77         if(y&1)
78             ans=ans*base%mod;
79         base=base*base%mod;
80         y>>=1;
81     }
82     return ans;
83 }
84 int main()
85 {
86     ios::sync_with_stdio(false);
87     cin>>n>>m;
88     init();
89     solve1();
90     solve2();
91     add(tot,n*n%mod*n%mod*m%mod*m%mod*m%mod-3*ans1%mod-n*m%mod);
92     add(ans,3*n*m%mod*ans1%mod-6*ans2%mod);
93     add(tot,mod);
94     add(ans,mod);
95     cout<<ans*qpow(tot,mod-2)%mod<<endl;
96     return 0;
97 }

原文地址:https://www.cnblogs.com/GreenDuck/p/10901549.html

时间: 2024-10-29 15:57:30

19_05_21校内训练[边上的整点]的相关文章

19_05_21校内训练[简单序列]

题意 定义一个序列的价值为其排序后所有位置(从1开始)乘以该位置元素的值的和,即∑i*ai.求一个长度为n的序列的所有连续子序列的价值和. 思考 一个序列的价值可看做所有的元素的和,加上所有无序二元组中较大的元素的值. 因此答案分为两部分:1.所有可能序列的元素和的和.考虑一个点,算出有多少区间包含它既可. 2.无序二元组较大的元素的值.不难得出两个点之间的贡献是线性的,因此用树状数组维护所有小于等于某个数的位置和既可.正反各做一遍. 代码 1 #include<bits/stdc++.h>

「csp校内训练 2019-10-24」解题报告

「csp校内训练 2019-10-24」解题报告 T1.猴猴吃苹果 \(Description\) 猴猴最喜欢在树上玩耍,一天猴猴又跳上了一棵树,这棵树有 \(N \ (N \leq 50000)\) 个苹果,每个苹果有一个编号,分别为 \(0\) ~ \(N - 1\) 它们之间由 \(N-1\) 个树枝相连,猴猴可以从树枝的一端爬到树枝的另一端,所以猴猴可以从任意一个苹果的位置出发爬到任意猴猴想去的苹果的位置. 猴猴开始在编号为 \(K \ (K < N)\) 的苹果的位置,并且把这个苹果吃

「csp校内训练 2019-10-30」解题报告

「csp校内训练 2019-10-30」解题报告 T1.树 题目链接(逃) \(Description\): 现在有一棵树,共 \(N\) 个节点. 规定:根节点为 \(1\) 号节点,且每个节点有一个点权. 现在,有 \(M\) 个操作需要在树上完成,每次操作为下列三种之一: \(1 \ x \ a\):操作 \(1\),将节点 \(x\) 点权增加 \(a\). \(2 \ x \ a\):操作 \(2\),将以节点 \(x\) 为根的子树中所有点的权值增加 \(a\). \(3 \ x\)

2017-4-7校内训练

丧病hzwer的ctsc训练赛 My AC:3/4 A.[Ctsc2014]企鹅QQ 思路:乱hash,我比较菜,写的丑代码各种WA+TLE,好久才A掉. #include<cstdio> #include<algorithm> using namespace std; #define ll long long #define MN 200 #define MX 6000000 #define MM 9000001 #define MOD1 890123798112473LL st

20170910校内训练

CCT 最近学校又发了n本五三题霸,BBS看到后十分高兴.但是,当他把五三拿到手后才发现,他已经刷过这些书了!他又认真地看了一会儿,发现新发的这些五三是2017版的,而他刷的是2016版的.现在他想找出所有他没有刷过的题来刷.每本五三都有m道题,并且它的特征(即它和去年版本的五三的差距)可以用一个m位二进制数来代表,二进制位上的1代表该题不同,0代表该题相同.比如4(100)就代表题目3和去年的有不同.5(101)就代表题目1和题目3和去年的有不同.而BBS热衷于给自己找麻烦,他要选择连续一段的

2017-3-3校内训练

hzwer出丧题虐人啦 ACM赛制 4/7 A.恼人的青蛙 题目大意:给定N*M矩阵上K个点,定义一条合法路径为从矩形外一点沿一条直线穿过矩形,每次走相同长度且在矩形内每步都要踩在给定点上,问经过给定点最多的路径经过几个点(若小于3输出0)(N,M,K<=5000). 思路:把点按横坐标第一关键字纵坐标第二关键字排序,f[i][j]表示有一条到i的路径,i上一个点是j,此时路径经过点数,每次确定i,j后就可以根据i,j算出j再前一个点的坐标,直接转移,复杂度O(K^2).评测机极慢稍微卡卡常才能

【三中校内训练】旅行

[题解] 显然的这是一道树形DP的题目 这里令f[i][0]为从i出发向以它为根的子树里走直到不能走的最大.最小价值 (不能走是什么自己阅读题目) 令s为x的儿子,w[i][j]为i和j之间的边的长度,则 f[x][0]=max(f[s][1]+w[s][i]) f[x][1]=min(f[s][0]+w[s][i]) 显然的通过这种方法,我们可以得到60分 可是,如何优化到线性呢 我们考虑一个节点x,x向某条边走的情况出现了很多次,浪费了很多时间 我们定义h[x][0/1]为从x出发,经过x的

校内训练0609 problem c

[题目大意] 给一棵树,求有多少条路径满足总和-最大值 是P的倍数 n<=10^5, P<=10^7 [题解] 一看就是点分治嘛 不考虑子树合并,考虑poj1741的做法,每次考虑经过重心的路径,用优先队列,从小到达添加并求答案即可. 容斥下. # include <queue> # include <stdio.h> # include <string.h> # include <iostream> # include <algorith

[3.10校内训练赛]

真的报警啦,hzwer又出一堆丧题虐人啦..... ------------------------------------------- A.[poj-1190]生日蛋糕 要做一个m层的蛋糕,每一层有高度和半径,且要分别比它上面的那一层的高度和半径大至少一,给定总体积n,求最小的侧面和顶上的面积之和m<=20,n<=10000 搜索....但是要加上比较强的剪枝. 1.如果此时的半径和高度无法建出剩余体积那么大的蛋糕,剪掉.这种情况我们不考虑半径和高度的减小,直接用((r-1)^2+(h-1