想了10多分鐘,暴力只有一個極大複雜度的想法,顯然不可過,但仍是寫了,而後就全TLE也是。。。意料之中c++
暴力很好寫,可是錯誤理解了Tim給的部分分的意思:先給了一個\(a_i\le 10^9\),而後部分分裏面沒有提到\(a_i\)的狀況,我就忽略了\(a_i\)的大小對答案統計的影響。。。git
換句話說,我當時要是寫了離散化,就是\(43\)分到手。算法
題目要求的輸出能夠分紅三個問題,第一個問題正確 的話能夠獲得這個點的\(25%\)的分數,前兩個問題正確的話能夠獲得該點\(50%\)的分數,三個問題全對能夠得到所有的分數函數
可是。。。由於出題人的\(SPJ\)寫的有點問題,即便只求了第一個問題,後面的兩個問題也須要輸出點什麼來知足\(SPJ\)的判斷方式。學習
出題人在給的\(PDF\)裏面點到了這個注意事項,可是是在整個\(PDF\)的最後一頁最後一行。。。優化
/* * 考慮把全部的建築先都變成同樣高 * 而後往回推,暴力統計全部的狀況 */ #include <cstdio> #include <algorithm> inline int read() { int n=0,w=1;register char c=getchar(); while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9')n=n*10+c-'0',c=getchar(); return n*w; } inline int min(int x,int y) {return x<y?x:y;} inline int max(int x,int y) {return x>y?x:y;} inline int abs(int x) {return x<0?-x:x;} const int N=1000001; int n,c,ans=2147483647,h[N],a[N]; void dfs() { int emp=(a[1]-h[1])*(a[1]-h[1]); for(int i=2;i<=n;++i) { emp+=abs(a[i]-a[i-1])*c; emp+=(a[i]-h[i])*(a[i]-h[i]); } ans=min(ans,emp); for(int i=1;i<=n;++i) if(a[i]!=h[i]) { --a[i]; dfs(); ++a[i]; } } int main() { freopen("construct.in","r",stdin); freopen("construct.out","w",stdout); int maxn=0; n=read(),c=read(); for(int i=1;i<=n;++i) maxn=max(maxn,h[i]=read()); for(int i=1;i<=n;++i) a[i]=maxn; dfs(); printf("%d",ans); fclose(stdin);fclose(stdout); return 0; }
/* * 23分應該能夠模擬搞過去 * 蔬菜種類數不超過200的部分應該能夠前綴和搞過去? */ #include <cstdio> inline int read() { int n=0,w=1;register char c=getchar(); while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9')n=n*10+c-'0',c=getchar(); return n*w; } inline int max(int x,int y) {return x>y?x:y;} const int N=201; int r,c,q,ans,maxn,map[N][N],newmap[N][N][N]; inline void work() { for(int i=1;i<=r;++i) for(int j=1;j<=c;++j) { for(int x=1;x<N;++x) newmap[x][i][j]=newmap[x][i-1][j]+newmap[x][i][j-1]-newmap[x][i-1][j-1]; ++newmap[map[i][j]][i][j]; } int x,y,xx,yy,ans,emp; while(q--) { ans=0; x=read(),y=read(),xx=read(),yy=read(); for(int i=1;i<N;++i) { emp=newmap[i][xx][yy]-newmap[i][xx][y-1]-newmap[i][x-1][yy]+newmap[i][x-1][y-1]; ans+=emp*emp; } printf("%d\n",ans); } } inline void solve() { int x,y,xx,yy,emp; while(q--) { ans=0; x=read(),y=read(),xx=read(),yy=read(); for(int k=1;k<=maxn;++k) { emp=0; for(int i=x;i<=xx;++i) for(int j=y;j<=yy;++j) if(map[i][j]==k) ++emp; ans+=emp*emp; } printf("%d\n",ans); } } int main() { freopen("vegetable.in","r",stdin); freopen("vegetable.out","w",stdout); r=read(),c=read(),q=read(); for(int x,i=1;i<=r;++i) for(int j=1;j<=c;++j) { map[i][j]=read(); maxn=max(maxn,map[i][j]); } if(q<=1000) solve(); else if(maxn<=200) work(); fclose(stdin);fclose(stdout); return 0; }
/* * 第二個問題不會。。。 * 只能拿25%*20了 */ #include <cstring> #include <cstdio> inline int read() { int n=0,w=1;register char c=getchar(); while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9')n=n*10+c-'0',c=getchar(); return n*w; } inline int min(int x,int y) {return x<y?x:y;} inline int max(int x,int y) {return x>y?x:y;} /* const int N=300001; struct Edge{ int v,nxt; }edge[N<<1]; int n,tot,maxn,ans,head[N];//,ansedge[N],top,anspoint[4]; bool mark[N<<1]; inline void add(int u,int v) {edge[++tot]=(Edge){v,head[u]};head[u]=tot;} void dfs(int now,int step,int fa) { maxn=max(maxn,step); for(int v,i=head[now];i;i=edge[i].nxt) if((v=edge[i].v)!=fa && !mark[i]) dfs(v,step+1,now); } */ const int N=3001; int n,maxn,ans=2147483647; bool map[N][N],vis[N]; void dfs(int now,int step,int fa) { vis[now]=true; maxn=max(maxn,step); for(int i=1;i<=n;++i) if(map[now][i] && !vis[i]) dfs(i,step+1,now); } int main() { freopen("league1.in","r",stdin); // freopen("league.out","w",stdout); n=read(); for(int u,v,i=1;i<n;++i) { u=read(),v=read(); map[u][v]=map[v][u]=true; } for(int i=1;i<=n;++i) for(int j=i+1;j<=n;++j) { if(!map[i][j])continue; map[i][j]=map[j][i]=false; for(int i=1;i<=n;++i) for(int j=i+1;j<=n;++j) { if(map[i][j])continue; map[i][j]=map[j][i]=true; maxn=0; dfs(1,0,0); for(int i=2;i<=n;++i) if(!vis[i]) goto E; for(int i=2;i<=n;++i) { dfs(i,0,0); memset(vis,false,sizeof vis); } ans=min(ans,maxn); E: map[i][j]=map[j][i]=false; } map[i][j]=map[j][i]=true; } /* for(int u,v,i=1;i<n;++i) { u=read(),v=read(); add(u,v),add(v,u); } for(int i=1;i<=tot;++i) { mark[i]=mark[i+1]=true; for(int j=1;j<=n;++j) for(int k=j+1;k<=n;++k) { add(j,k), */ /* for(int i=1;i<=tot;++++i) { mark[i]=true; for(int j=1;j<=n;++j) for(int k=j+1;k<=n;++k) { add(j,k),add(k,j); maxn=0; for(int l=1;l<=n;++l) dfs(l,0,0); if(ans>maxn) { ans=maxn; ansedge[top=1]=i; anspoint[0]=edge[i].v; anspoint[1]=edge[i^1].v; anspoint[2]=j; anspoint[3]=k; } head[k]=edge[tot--].nxt; head[j]=edge[tot--].nxt; } mark[i]=false; }*/ printf("%d",ans); fclose(stdin);fclose(stdout); return 0; }
記\(f_i\)表示考慮前\(i\)個建築,而且第\(i\)個建築的高度不變的答案,每次轉移的時候枚舉上一個不變的建築編號,中間的一段必定變成相同的高度,而且高度小於等於兩端的高度ui
假設從\(f_j\)轉移而且中間高度是\(t\),那麼\(f_i=\sum_{k=j+1}^{i-1}(t-h_k)^2+c(h[j]+h[i]-2t)\)spa
這樣中間的高度能夠\(O(1)\)求二次函數的對稱軸debug
考慮優化轉移,由於中間的高度小於兩端,因此最多有\(h_j\gt h_i\)的\(j\)可以轉移,能夠維護關於高度的單調棧,那麼有效的轉移次數就是\(O(N)\)code
#include <bits/stdc++.h> using std::pair; using std::vector; using std::string; typedef long long ll; typedef pair<int, int> pii; #define fst first #define snd second #define pb(a) push_back(a) #define mp(a, b) std::make_pair(a, b) #define debug(...) fprintf(stderr, __VA_ARGS__) template <typename T> bool chkmax(T& a, T b) { return a < b ? a = b, 1 : 0; } template <typename T> bool chkmin(T& a, T b) { return a > b ? a = b, 1 : 0; } const int oo = 0x3f3f3f3f; string procStatus() { std::ifstream t("/proc/self/status"); return string(std::istreambuf_iterator<char>(t), std::istreambuf_iterator<char>()); } template <typename T> T read(T& x) { int f = 1; x = 0; char ch = getchar(); for(;!isdigit(ch); ch = getchar()) if(ch == '-') f = -1; for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48; return x *= f; } const int N = 1000000; int n, C; int h[N + 5]; ll s[2][N + 5], dp[N + 5]; ll solve(int x, int y, int mx) { ll a = y - x - 1; ll b = -2 * (s[0][y-1] - s[0][x]) - (x != 0) * C - (y != n+1) * C; ll c = s[1][y-1] - s[1][x] + 1ll * (x != 0) * h[x] * C + 1ll * (y != n+1) * h[y] * C; ll t; t = (ll) std::round(-1. * b / 2 / a); chkmax<ll>(t, mx); if(x != 0) chkmin(t, (ll) h[x]); if(y <= n) chkmin(t, (ll) h[y]); return a * t * t + b * t + c; } int main() { freopen("construct.in", "r", stdin); freopen("construct.out", "w", stdout); read(n), read(C); for(int i = 1; i <= n; ++i) { read(h[i]); s[0][i] = s[0][i-1] + h[i]; s[1][i] = s[1][i-1] + 1ll * h[i] * h[i]; } static int stk[N + 5], top; h[0] = h[n + 1] = oo; stk[top ++] = 0; for(int i = 1; i <= n+1; ++i) { dp[i] = dp[i-1] + ((i == 1 || i == n+1) ? 0 : 1ll * C * std::abs(h[i] - h[i-1])); while(top > 0 && h[stk[top-1]] <= h[i]) { if(top > 1) chkmin(dp[i], dp[stk[top-2]] + solve(stk[top-2], i, h[stk[top-1]])); -- top; } stk[top ++] = i; } printf("%lld\n", dp[n+1]); return 0; }
當蔬菜的出現次數比較多的時候能夠對每種蔬菜維護二維前綴和而且根據定義計算答案
當蔬菜的出現次數比較少的時候考慮平方的轉化,至關於計算有多少個點被詢問區域包含,實際上等價於四維偏序
綜合分析兩種算法的複雜度而且選取合適的出現次數分界值\(k\),最終複雜度爲\(O(\frac{n^2}{k}(n^2+q)+(n^2k+q)\log^3 n)\),那麼根據這個式子可知\(k=\sqrt{\frac{n^2+q}{\log^3 n}}\)的時候最優
#include <bits/stdc++.h> using std::pair; using std::vector; using std::string; typedef long long ll; typedef pair<int, int> pii; #define fst first #define snd second #define pb(a) push_back(a) #define mp(a, b) std::make_pair(a, b) #define debug(...) fprintf(stderr, __VA_ARGS__) template <typename T> bool chkmax(T& a, T b) { return a < b ? a = b, 1 : 0; } template <typename T> bool chkmin(T& a, T b) { return a > b ? a = b, 1 : 0; } const int oo = 0x3f3f3f3f; string procStatus() { std::ifstream t("/proc/self/status"); return string(std::istreambuf_iterator<char>(t), std::istreambuf_iterator<char>()); } template <typename T> T read(T& x) { int f = 1; x = 0; char ch = getchar(); for(;!isdigit(ch); ch = getchar()) if(ch == '-') f = -1; for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48; return x *= f; } const int K = 40; const int N = 200; const int Q = 100000; int n, m, q; struct BIT { #define lowbit(x) (x & -x) int c[N + 5][N + 5][N + 5]; void add(int x, int y, int z) { for(int i = x; i <= m; i += lowbit(i)) for(int j = y; j <= n; j += lowbit(j)) for(int k = z; k <= m; k += lowbit(k)) ++ c[i][j][k]; } int query(int x, int y, int z) { int res = 0; for(int i = x; i > 0; i -= lowbit(i)) for(int j = y; j > 0; j -= lowbit(j)) for(int k = z; k > 0; k -= lowbit(k)) res += c[i][j][k]; return res; } } bit; struct query { int x1, y1, x2, y2, id; void input(int _id) { id = _id; read(x1), read(y1); read(x2), read(y2); } bool operator < (const query& rhs) const { return x1 > rhs.x1; } }; vector <int> d; vector <query> mod; vector <pii> p[Q + 5]; query que[Q + 5]; int ans[Q + 5], cnt[N*N + 5]; int a[N + 5][N + 5], b[N + 5][N + 5]; inline int pw2(int x) { return x * x; } void calc(int col) { for(int i = 1; i <= n; ++i) { for(int j = 1; j <= m; ++j) b[i][j] = b[i][j-1] + (a[i][j] == col); for(int j = 1; j <= m; ++j) b[i][j] = b[i][j] + b[i-1][j]; } for(int i = 1; i <= q; ++i) { int x1 = que[i].x1, y1 = que[i].y1; int x2 = que[i].x2, y2 = que[i].y2; ans[que[i].id] += pw2(b[x2][y2] - b[x1-1][y2] - b[x2][y1-1] + b[x1-1][y1-1]); } } void input() { read(n), read(m); read(q); for(int i = 1; i <= n; ++i) for(int j = 1; j <= m; ++j) d.pb(read(a[i][j])); for(int i = 1; i <= q; ++i) que[i].input(i); } void solve() { std::sort(d.begin(), d.end()); d.erase(std::unique(d.begin(), d.end()), d.end()); for(int i = 1; i <= n; ++i) for(int j = 1; j <= m; ++j) { a[i][j] = std::lower_bound(d.begin(), d.end(), a[i][j]) - d.begin(); ++ cnt[a[i][j]]; p[a[i][j]].pb(mp(i, j)); } for(int i = 0; i < (int) d.size(); ++i) { if(cnt[i] >= K) { calc(i); } else { for(auto x : p[i]) for(auto y : p[i]) { query temp; temp.x1 = x.fst, temp.y1 = x.snd; temp.x2 = y.fst, temp.y2 = y.snd; if(temp.x1 > temp.x2) std::swap(temp.x1, temp.x2); if(temp.y1 > temp.y2) std::swap(temp.y1, temp.y2); mod.pb(temp); } } } std::sort(que + 1, que + q + 1); std::sort(mod.begin(), mod.end()); for(int i = 1, j = 0; i <= q; ++i) { while(j < (int) mod.size() && mod[j].x1 >= que[i].x1) { bit.add(m - mod[j].y1 + 1, mod[j].x2, mod[j].y2); ++ j; } ans[que[i].id] += bit.query(m - que[i].y1 + 1, que[i].x2, que[i].y2); } for(int i = 1; i <= q; ++i) printf("%d\n", ans[i]); } int main() { freopen("vegetable.in", "r", stdin); freopen("vegetable.out", "w", stdout); input(); solve(); return 0; }
顯然危險程度就是樹的直徑,斷開邊以後會獲得兩個聯通塊,假設兩個聯通塊的直徑分別爲\(l_1,l_2\),根據直徑的性質,鏈接兩個聯通塊以後新的直徑長度最小是\(\max\{l_1,l_2,\lceil \frac{l_1}{2}\rceil+\lceil\frac{l_2}{2}\rceil+1\}\)
而後考慮維護,因爲合併兩個聯通塊以後新的直徑的兩個端點必定會在原來的直徑端點的並中產生,能夠處理每個子樹的直徑端點,每次考慮\(i\)到\(f_{a_i}\)的邊時只須要分別求出兩個塊的直徑端點便可,在每一個點上維護子樹前綴聯通塊和後綴聯通塊的直徑端點便可快速合併
#include <bits/stdc++.h> using std::pair; using std::vector; using std::string; typedef long long ll; typedef pair<int, int> pii; #define fst first #define snd second #define pb(a) push_back(a) #define mp(a, b) std::make_pair(a, b) #define debug(...) fprintf(stderr, __VA_ARGS__) template <typename T> bool chkmax(T& a, T b) { return a < b ? a = b, 1 : 0; } template <typename T> bool chkmin(T& a, T b) { return a > b ? a = b, 1 : 0; } const int oo = 0x3f3f3f3f; string procStatus() { std::ifstream t("/proc/self/status"); return string(std::istreambuf_iterator<char>(t), std::istreambuf_iterator<char>()); } template <typename T> T read(T& x) { int f = 1; x = 0; char ch = getchar(); for(;!isdigit(ch); ch = getchar()) if(ch == '-') f = -1; for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48; return x *= f; } const int N = 300000; struct diameter { int x, y, d; }; int sz[N + 5]; int fa[N + 5][21], dep[N + 5]; int st[N + 5], to[(N << 1) + 5], nxt[(N << 1) + 5], e = 1; inline void addedge(int u, int v) { to[++ e] = v; nxt[e] = st[u]; st[u] = e; } int get_up(int x, int d) { for(int i = 0; d > 0; ++i, d >>= 1) if(d & 1) x = fa[x][i]; return x; } int get_lca(int x, int y) { if(dep[x] < dep[y]) std::swap(x, y); for(int i = 0, d = dep[x] - dep[y]; d > 0; ++i, d >>= 1) if(d & 1) x = fa[x][i]; if(x == y) return x; for(int i = 20; i >= 0; --i) if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i]; return fa[x][0]; } inline int get_dis(int x, int y, int lca = 0) { if(lca) return dep[x] + dep[y] - 2*dep[lca]; return dep[x] + dep[y] - 2*dep[get_lca(x, y)]; } inline int get_mid(int x, int y) { int r = get_lca(x, y), dis = get_dis(x, y) >> 1; return (dep[x] - dep[r] >= dis) ? get_up(x, dis) : get_up(y, dis); } inline void merge(diameter& a, diameter b) { int len = a.d, x = a.x, y = a.y; if(chkmax(len, b.d)) a.x = b.x, a.y = b.y; if(chkmax(len, get_dis(x, b.x))) a.x = x, a.y = b.x; if(chkmax(len, get_dis(x, b.y))) a.x = x, a.y = b.y; if(chkmax(len, get_dis(y, b.x))) a.x = y, a.y = b.x; if(chkmax(len, get_dis(y, b.y))) a.x = y, a.y = b.y; a.d = len; } diameter sub[N + 5]; void dfs(int u, int f = 0) { fa[u][0] = f; dep[u] = dep[f] + 1; sub[u] = (diameter) { u, u, 0 }; for(int i = 1; i < 21; ++i) fa[u][i] = fa[fa[u][i-1]][i-1]; sz[u] = 1; for(int i = st[u]; i; i = nxt[i]) { int v = to[i]; if(v == f) continue; dfs(v, u); sz[u] ++; merge(sub[u], sub[v]); } } int ans = oo; int length[N + 5]; int sx, sy, tx0, tx1, ty0, ty1; pair<diameter, int> *pre[N + 5]; void dfs1(int u, diameter lst, int f = 0) { if(f > 0) { int len = std::max(std::max(lst.d, (lst.d + 1) / 2 + (sub[u].d + 1) / 2 + 1), sub[u].d); if(chkmin(ans, length[u-1] = len)) { sx = u, sy = f; tx0 = lst.x, tx1 = lst.y; ty0 = sub[u].x, ty1 = sub[u].y; } } int c = 0; pre[u] = new pair<diameter, int> [sz[u] + 5]; pre[u][c ++] = mp(lst, -1); for(int i = st[u]; i; i = nxt[i]) { int v = to[i]; if(v == f) continue; diameter x = pre[u][c-1].fst; merge(x, sub[v]); pre[u][c ++] = mp(x, v); } diameter suf = lst, temp; merge(suf, (diameter) { u, u, 0 }); for(int i = c - 1; i > 0; --i) { int v = pre[u][i].snd; merge(temp = suf, pre[u][i-1].fst); dfs1(v, temp, u); merge(suf, sub[v]); } } int n; int main() { freopen("league.in", "r", stdin); freopen("league.out", "w", stdout); read(n); for(int i = 1; i < n; ++i) { static int u, v; read(u), read(v); addedge(u, v); addedge(v, u); } dfs(1); dfs1(1, (diameter) { 1, 1, 0 }); vector<int> plan; for(int i = 1; i < n; ++i) if(length[i] == ans) plan.pb(i); printf("%d\n", ans); printf("%lu ", plan.size()); for(auto v : plan) printf("%d ", v); printf("\n%d %d %d %d\n", sx, sy, get_mid(tx0, tx1), get_mid(ty0, ty1)); return 0; }
\(gcd(x^a-1,x^b-1)=x^{gcd(a,b)}-1\)
證實:
不妨假設\(a\gt b\),那麼必定有\(a=b\times k+t(k,t\in N_+)\)
那麼\(gcd(x^{bk+t}-1,x^b-1)=gcd(x^{bk}\times x^t-1,x^b-1)=gcd((x^b)^k\times x^t-1,x^b-1)\)
\(gcd(fib_a,fib_b)=fit_{gcd(a,b)}\)
如有\(\gcd(a,p)=1\),那麼\(a^\varphi(p)\equiv 1\pmod p\)
通常狀況:\(a^t\equiv a^{\min(t,t\mod \varphi(p)+\varphi(p))}\pmod p\)
形如\(x\equiv a_i\pmod {p_i}\)
由此拓展出中國剩餘定理
若\(p_i\)兩兩互質,則存在通解
\[P=\prod_i p_i\]
\[P_i=\frac{P}{p_i}\]
\[T_i=P_i^{-1}\mod p_i\]
\[x\equiv\sum_i a_iT_iP_i\]
其拓展形式:
考慮合併兩個方程:
\[ \begin{aligned} x &\equiv a_1 \pmod{p_1} \\ x &\equiv a_2 \pmod{p_2} \\ x &= a_1 + k_1p_1 = a_2 + k_2p_2 \\ \end{aligned} \]
令\(t=\gcd(p_1,p_2)\),則有
\[ \begin{aligned} k_1\frac{p_1}{t}&\equiv \frac{a_2 - a_1}{t} \pmod{\frac{p_2}{t}}\\ k_1 &\equiv \frac{a_2 - a_1}{t} \times (\frac{p_1}{t})^{-1} \pmod{\frac{p_2}{t}} \\ x &\equiv a_1 + p_1 \left(\frac{a_2 - a_1}{t} \times (\frac{p_1}{t})^{-1} \bmod \frac{p_2}{t} \right) \pmod{\frac{p_1p_2}{t}} \end{aligned} \]
令\(n = p_1^{e_1}p_2^{e_2} \cdots p_k^{e_k}\):
\[(f*g)(n) = \sum_{d|n} f(d) g(\frac{n}{d})\]
\(\mu * 1 = \epsilon\)
\(\mathrm{Id} = \varphi * 1 \Rightarrow \varphi = \mathrm{Id} * \mu\)
\(\mathrm{d} = 1 * 1 \Rightarrow 1 = \mu * \mathrm{d}\)
\(\sigma = \mathrm{Id} * 1 \Rightarrow \mathrm{Id} = \mu * \sigma \Rightarrow \sigma = \varphi * \mathrm{d}\)
\(d(ij) = \sum_{x|i}\sum_{y|j} [\gcd(x, y) = 1]\)
\(\sigma(ij) = \sum_{x|i}\sum_{y|j} [\gcd(x, y) = 1] \frac{iy}{x}\)
狄利克雷卷積知足交換律, 結合律, 兩個積性函數的卷積也是積性的
經過一個例題來說解
計算:\[{n \choose m} \bmod p\]
\(n, m \le 5000\)
\(n, m \le 10^6, p\) 是質數
\(n, m \le 10^{18}, p \le 10^6, p\) 是質數
\(n, m \le 10^{18}, p \le 10^6\).
第一個部分分:暴力
第二個部分分:預處理階乘而後算
第三個部分分:Lucas定理
第四個部分分:擴展Lucas定理
\[ \begin{aligned} & \sum_{i=0}^{n} {n \choose i} = 2 ^ n \\ & \sum_{i=0}^{n} {i \choose x} = {n+1 \choose x+1} \\ & \sum_{i=0}^{n} {k+i \choose i} = {k+n+1 \choose n} \\ & \sum_{i=0}^{m} {m \choose i} {n-m \choose m-i} = {n \choose m} \end{aligned} \]
定義: \(\begin{bmatrix} n \\ m \end{bmatrix}\) 表示將\(n\) 個物品分爲 \(m\) 個無序非空環的方案數
遞推式:\[\begin{bmatrix} n \\ m \end{bmatrix} = \begin{bmatrix} n-1 \\ m-1 \end{bmatrix} + (n-1)\begin{bmatrix} n-1 \\ m \end{bmatrix}\]
生成函數:
\[ \begin{aligned} & x^{\overline{n}} = \sum_{i=0}^{n} \begin{bmatrix} n \\ i \end{bmatrix} x^i \\ & x^{\underline{n}}= \sum_{i=0}^{n} (-1)^{n-i} \begin{bmatrix} n \\ i \end{bmatrix} x^i \end{aligned} \]
定義: \(n \brace m\) 表示將 \(n\) 個物品分紅 \(m\) 個無序非空集合的方案數
\[{n \brace m} = {n-1 \brace m-1} + m{n-1 \brace m}\]
生成函數:
\[ \begin{aligned} & x^n = \sum_{i=0}^{n} {n \brace i} x^{\underline{i}} \\ & m!{n \brace m} = \sum_{i=0}^{m} (-1)^{m-i} {m \choose i} i^n \end{aligned} \]
定義 \(\Delta^{k} f(n)\) 爲 \(f(n)\) 的 \(k\) 階差分序列, 而且:
\[\Delta^{k} f(n) = \begin{cases} f(n), & \mathrm{k = 0} \\ \Delta^{k-1} f(n+1) - \Delta^{k-1} f(n), & \mathrm{otherwise} \end{cases} \]
容易發現差分序列的具備線性性, 特別地, 多項式:
\[f(x) = \{0, 0, 0, \cdots, 1\} \mid x \in [0, n]\]
差分序列的第一列是 \(\{0, 0, 0, \cdots, 1\}\) , 事實上這個多項式就是
\[\frac{1}{n!} \prod_{i=0}^{n-1} (x - i) = {x \choose n}\]
已知一個 \(n\) 次多項式的 \(n+1\) 個點值, 求這個多項式的係數表示
求出多項式的 \(n\) 階差分序列第一列 \(\{c_i\}\), 能夠將多項式表示成:
\[f(x) = \sum_{i=0}^{n} c_i {x \choose i}\]
考慮構造一個通過全部給定點的多項式:
\[ \begin{aligned} g_i(x) = \prod_{j=0, j \neq i}^{n} \frac{x - x_j}{x_i - x_j} \\ f(x) = \sum_{i=0}^{n} y_ig_i(x) \end{aligned} \]
求\[f_k(n) = \sum_{i=0}^{n} i^k\]
解:
直接用第二類斯特林數展開:
\[ \begin{aligned} f_k(n) &= \sum_{i=0}^{n} \sum_{j=0}^{k} {k \brace j} i^{\underline{j}} \\ &= \sum_{j=0}^{k} {k \brace j} j! \sum_{i=0}^{n} {i \choose j} \\ &= \sum_{j=0}^{k} {k \brace j} j! {n + 1 \choose j + 1} \end{aligned} \]
或者用拉格朗日插值
經過上一個方法咱們知道 \(f_k(n)\) 是一個關於 \(n\) 的 \(k+1\) 次多項式,
因而直接拉格朗日插值便可, 而且因爲係數的特殊性質, 能夠作到 \(O(k)\)
另外:
Bernoulli數
定義 \(B_i\) 爲伯努利數, 知足:
\[\sum_{i=0}^{m} B_i {m+1 \choose i} = [m = 0]\]
那麼有:
\[f_k(n-1) = \frac{1}{k+1} \sum_{i=0}^{k} B_i n^{k + 1 - i} {k + 1 \choose i}\]
\(\mathrm{Min-Max}\) 容斥:\[\max(S) = \sum_{T \subseteq S, T \neq \varnothing} \min(T) ^ {(-1) ^ {|T|-1}}\]
拓展形式:\[\mathrm{lcm}(S) = \prod_{T \subseteq S, T \neq \varnothing} \gcd(T) ^ {(-1) ^ {|T|-1}}\]
二項式反演:
\[ \begin{aligned} f(n) &= \sum_{i=0}^{n} {n \choose i} g(i)\\ \Leftrightarrow g(n) &= \sum_{i=0}^{n} (-1)^{n-i} {n \choose i} f(i) \end{aligned} \]
Stirling反演:
\[ \begin{aligned} f(n) &= \sum_{i=0}^{n} {n \brace i} g(i) \\ \Leftrightarrow g(n) &= \sum_{i=0}^{n} (-1)^{n-i} \begin{bmatrix} n \\ i \end{bmatrix} f(i) \end{aligned} \]
已知:
\[ \begin{aligned} f(n) &= \begin{cases} 0, & \textrm{n = 0} \\ 1, & \textrm{n = 1} \\ 2f(n-1) + f(n-2), & \textrm{otherwise} \\ \end{cases} \\ g(n) &= \mathrm{lcm}(f(1), f(2), \cdots, f(n)) \end{aligned} \]
求:\[\sum_{i=1}^{n} g(i) \times i\]
\(n \le 10^6\)
解:
首先有 \(\gcd(f(i), f(j)) = f(\gcd(i, j))\)
根據 \(\mathrm{Min-Max}\) 容斥有:
\[ \begin{aligned} g(n) &= \prod_{T \subseteq S, T \neq \varnothing} \gcd(T)^{(-1)^{|T|+1}} \\ &= \prod_{T \subseteq S, T \neq \varnothing} f(\gcd(T))^{(-1)^{|T|+1}} \end{aligned} \]
\[ \begin{aligned} f(n) &= \prod_{d|n} h(d) \\ \Rightarrow g(n) &= \prod_{d=1}^{n} h(d)^{\sum_{T \subseteq S, T \neq \varnothing} [d \mid gcd(T)] (-1)^{|T|+1}} \\ &= \prod_{d=1}^{n} h(d) \end{aligned} \]
給出一個 \(n \times m\) 大小的矩形,
每一個位置能夠填上 \([1,c]\) 中的任意一個數,
要求填好後任意兩行互不等價且任意兩列互不等價,
兩行或兩列等價當且僅當對應位置徹底相同, 求方案數
\(n, m \le 5000\)
解:
首先咱們有一個很簡單的方式使得列之間互不等價, 對於任意一列,總方案數是 \(c^n\), 那麼使得列與列之間互不相同的方案數爲 \((c^n)^{\underline{m}}\)
接下來的問題只與行數有關,
定義 \(g(n)\) 表示 \(n\) 行不保證每行互不等價的方案數,\(f(n)\) 表示 \(n\) 行保證任意兩行互不等價的方案數, 有:
\[ \begin{aligned} g(n) &= (c^n)^{\underline{m}} \\ &= \sum_{i=0}^{n} {n \brace i} f(i) \\ f(n) &= \sum_{i=0}^{n} (-1)^{n-i} \begin{bmatrix} n \\ i \end{bmatrix} g(i) \end{aligned} \]
給出一個長度爲 \(n\) 的序列 \(\{a_i\}\) 以及一個數 \(p\), 如今有 \(m\) 次操做,每次操做將 \([l, r]\) 區間內的 \(a_i\) 變成 \(c^{a_i}\), 或者詢問 \([l, r]\) 之間全部 \(a_i\) 的和對 \(p\) 取模的結果
\(n, m \le 5 \times 10^4, p \le 2^{14}\)
解:
對於修改操做能夠利用拓展歐拉定理, 維護一個 \(\log(p)\) 層的結構表示每個 \(a_i\) 的值
因爲通過只有最後的 \(\log(p)\) 次操做是有效的,因此任意的兩個相鄰位置在通過 \(\log(p)\) 次操做後會變得等價,在最外層維護一個 std::set
記錄等價的區間, 而後用線段樹作詢問