[SDOI2008] 儀仗隊
阿新 • • 發佈:2018-10-06
algorithm 坐標 不能 def 記得 min long 線段 ref 即可
題目類型:莫比烏斯反演/歐拉函數??
傳送門:>Here<
題意:有一個\(N*N\)的方陣,問左下角那個人可以看到幾個人?(光沿直線傳播)
解題思路
考慮什麽時候一個人會看不見?那就是和別的一條視線重合的時候。又由於線段的另一個端點是確定的,因此視線會重合當且僅當
斜率相同。
因此題目就是在問我們有幾個不同的斜率。設一個人的位置為\((i,j)\),則他的斜率是\(\dfrac{j}{i}\)。要使斜率不同,其實也就
是此分數不得被約分。為什麽?因為如果能被約分就對應另外一個人的坐標了!
因此題目也就是轉化為求在\(N\)的範圍內互質整數對的個數(互質則不能約分)。因此莫比烏斯反演
然而我們默認了原點是\((0,0)\),因此先將那塊右上角求好,讓現在的\((1,1)\)去當\((0,0)\),然後加上\((0,1)\)和\((1,0)\)則兩個
點。註意,這時候我們已經把\(N\)減了1了。
反思
這題的關鍵在於想怎樣會看不到,而不是怎樣才能看到。有時正著想不出來,反著想更簡單
另外,考慮邊界條件的時候常常涉及到+1-1,這時候要大膽猜想,小心驗證。
Code
篩莫比烏斯函數的時候要記得判斷超界。(就是這樣\(RE\)的)
/*By DennyQi 2018*/ #include <cstdio> #include <queue> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int MAXN = 400010; const int INF = 1061109567; inline int Max(const int a, const int b){ return (a > b) ? a : b; } inline int Min(const int a, const int b){ return (a < b) ? a : b; } inline int read(){ int x = 0; int w = 1; register char c = getchar(); for(; c ^ '-' && (c < '0' || c > '9'); c = getchar()); if(c == '-') w = -1, c = getchar(); for(; c >= '0' && c <= '9'; c = getchar()) x = (x<<3) + (x<<1) + c - '0'; return x * w; } int N,ans; int mu[MAXN],prime[MAXN],b[MAXN],tot; inline void getMobius(){ mu[1] = 1; for(int i = 2; i <= N; ++i){ if(!b[i]){ prime[++tot] = i; mu[i] = -1; } for(int j = 1; j <= tot; ++j){ if(i * prime[j] > N) break; b[i * prime[j]] = 1; if(i % prime[j] == 0){ mu[i * prime[j]] = 0; break; } mu[i * prime[j]] = -mu[i]; } } } int main(){ N = read()-1; if(N <= 0){ printf("0"); return 0; } getMobius(); for(int i = 1; i <= N; ++i){ ans += mu[i] * (N/i) * (N/i); } printf("%d", ans+2); return 0; }
[SDOI2008] 儀仗隊