計蒜客 聯想的顯示屏校準 莫比烏斯反演

2022-05-02 02:21:06 字數 1973 閱讀 5831

先來乙個題面:

題目要我們計算在n*m的點陣中不同的直線有多少條。

顯然平行於座標軸的直線只有n+m條,所以我們只需要考慮不平行於座標軸的。

我們列舉直線的方向向量(a,b),且令a,b>0,那麼每一條這樣的直線通過繞垂直軸翻轉都能一一對應一條另一方向的直線(顯然,自行腦補就行了)。

然後,我們定義乙個點(x,y)的前驅為(x−a,y−b),後繼為(x+a,y+b),則直線的數量為滿足「它本身以及它的前驅在點陣內而它的後繼不在點陣內」的點的數量。

就是下面這個式子。

官方的式子就講到這裡了,下面我們來想想這個鬼畜的東西怎麼計算。

我們先考慮沒有max的那一項,有max的計算方法相同,只是更改一下係數罷了。

對於n-1/d,m-1/d這樣的東西,我們可以o(sqrt)地求解。

然後,對於後面乘上的那個東西與μ(d)的積,我們需要找出別的計算方法。

我們可以先線性地篩出μ(d)*d的字首和,再o(sqrt)地列舉n-1/d,求解這個式子。

對於最後的那個交叉乘項,也這樣做,無非就是更複雜一些。

然後發現我們只要預處理出來μ(d)*d*d的字首和就可以做啦!

最後,原式變成了:

對於第二個式子,我們也可以類似地反演,只是要修改係數。注意只有當a<=n/2且b<=m/2的時候有值,所以需要更改一下計算範圍qaq。最後反演出來大概是這樣的。

注意此時的min』=min(a/2,b/2),然而n、m還得帶入原來的值。也就是說,我們縮小了d的列舉範圍卻沒有修改nm的範圍。(注意如果直接令n/=2,m/=2會wa。這很顯然,自己想想為什麼)

最後上**:

1 #include2 #include3 #include4 #include5

#define lli long long int

6#define debug cout

7using

namespace

std;

8const

int maxn=4e5+1e2;

9const

int mod=1

<<30;10

11 lli sum[maxn][3

];12

lli ans;

1314

inline lli mod(lli x)

1718 inline void

gen()

28for(int j=1;j<=cnt&&(lli)i*prime[j]) 36}

37}38for(int i=1;i) 43}

4445 inline lli calc_g(int n,int

m) 52

return

mod( ret );53}

54 inline lli calc_mul(int n,int

m) 64

return

mod( ret );65}

66 inline lli calc_add(int n,int m,int fn,int

fm)

77return

mod( ret );78}

79 inline void getans(int n,int

m) 91

intnextchar()

96 inline int

getint()

100while(ch>='

0'&&ch<='9'

)101 ret=ret*10+(ch-'0'

),102 ch=nextchar();

103return ret*fix;

104}

105106

intmain()

116117

return0;

118 }

view code

顯示屏輸出(計蒜客)

複製出錯,格式有問題。不要描述部分了,看輸入樣例就能理解題意 輸入格式 輸入兩個整數 k,n 1 k 10 0 n 999 9999 9 分別表達放大的倍數和需要輸出的數字。輸出數碼管顯示的數字,數字每一位之間用一列空格隔開。樣例輸入12 12345 樣例輸出1 樣例輸入23 67890 樣例輸出2...

聯想的顯示屏校準(困難)

這題的關鍵在於推公式。推出公式並化簡後,中等和困難都可以做了。之前推了乙個公式 for int i 1 i for int j 1 j 然後做中等難度的時候用容斥來了一發。妹的,為了n 2維護字首花了接近一天。int sum n memset sum,0,sizeof sum for int n 2...

計蒜客 單獨的數字

給定乙個陣列 a,除了乙個數出現一次之外,其餘數都出現三次。找出出現一次的數。如 找出 7。你的演算法只能是線性時間的複雜度,並且不能使用額外的空間哦 輸入格式 第一行輸入乙個數n 1 n 500 代表陣列的長度。接下來一行輸入 n 個 int 範圍內 2147483648 2147483647 的...