常見套路?


1.一部分需要求類似於\(\sum ans(x)^k\),k不大,\(ans(x)\)貢獻每次多\(1\)

解:這種題感覺突然很常見還比較套路,在聯賽出現也不是不可能/kk

直接二項式定理可以做到\(O(k^2)\)更新貢獻,但是並不優秀。

考慮斯特林數展開,\(x^k=\sum_{i=0}^k\begin{Bmatrix}k\\i\end{Bmatrix}i!\begin{pmatrix}x\\i\end{pmatrix}\)

如何理解?考慮組合意義,\(x^k\)\(x\)個集合放\(k\)個數的方案數(集合可以為空),那么可以枚舉非空集,\(\begin{Bmatrix}k\\i\end{Bmatrix}\)\(i\)個集合放\(k\)個數(集合不可以為空),盒子不一樣所以乘\(i!\)\(\begin{pmatrix}x\\i\end{pmatrix}\)是從\(x\)個集合選\(i\)個非空集。

於是這個式子變為\(\sum_{i=0}^k\begin{Bmatrix}k\\i\end{Bmatrix}i!\sum\begin{pmatrix}ans(x)\\i\end{pmatrix}\)

發現每次貢獻更新只會跟\(\begin{pmatrix}ans(x)\\i\end{pmatrix}\)有關,而貢獻多\(1\)后,\(\begin{pmatrix}ans(x)+1\\i\end{pmatrix}=\begin{pmatrix}ans(x)\\i\end{pmatrix} + \begin{pmatrix}ans(x)\\i-1\end{pmatrix}\),於是我們維護\(\begin{pmatrix}ans(x)\\i\end{pmatrix},0\le i\le k\),每次更新貢獻可以\(O(k)\)完成。

2.拉格朗日恆等式。

感覺用的不多?寫下吧

\[(\sum_{i=1}^na_i^2)(\sum_{i=1}^nb_i^2)=(\sum_{i=1}^na_ib_i)^2+\sum_{1\le i<j\le n}(a_ib_j-a_jb_i)^2 \]

證明:

\[\begin{aligned} &(\sum_{i=1}^na_i^2)(\sum_{i=1}^nb_i^2)-(\sum_{i=1}^na_ib_i)^2\\ =&\sum_{i=1}^n\sum_{j=1}^na_i^2b_j^2-\sum_{i=1}^n\sum_{j=1}^na_ib_ia_jb_j\\ =&\frac{1}{2}\sum_{i=1}^n\sum_{j=1}^n(a_ib_j-a_jb_i)^2\\ =&\sum_{1\le i<j\le n}(a_ib_j-a_jb_i)^2 \end{aligned}\]

證畢。

然后這個可以用來證明柯西不等式,其實上面也已經證完了。

注意到

\[\sum_{1\le i<j\le n}(a_ib_j-a_jb_i)^2\ge0 \]

那么有

\[(\sum_{i=1}^na_i^2)(\sum_{i=1}^nb_i^2)-(\sum_{i=1}^na_ib_i)^2\ge0 \]

\[(\sum_{i=1}^na_i^2)(\sum_{i=1}^nb_i^2)\ge(\sum_{i=1}^na_ib_i)^2 \]

證畢。

貌似還有個更一般的式子?(后面的字母都是向量)

\[(\alpha\times\beta)\cdot(\tau\times\gamma)=\left|\begin{matrix}\alpha\cdot\tau\quad \alpha\cdot\gamma\\\beta\cdot\tau\quad\beta\cdot\gamma\end{matrix}\right| \]

證明:

向量的混合積:\([a,b,c]=(a\times b)\cdot c\)

\([a,b,c]\)的集合意義就是以\(a,b,c\)為鄰邊的平行六面體體積

混合積性質:\([a,b,c]=[b,c,a]=[c,a,b]\)

雙重外積公式:\((a\times b)\times c=b(a\times c)-a(b\times c)\)

然后對式子往后化簡:

\[\begin{aligned} &(\alpha\times\beta)\cdot(\tau\times\gamma)\\=&[\alpha,\beta,\tau\times\gamma]\\=&[\tau\times\gamma,\alpha,\beta]\\=&((\tau\times\gamma)\alpha)\cdot\beta\\=&((\tau\cdot\alpha)\gamma-(\gamma\cdot\alpha)\tau)\cdot\beta\\=&\left|\begin{matrix}\alpha\cdot\tau\quad \alpha\cdot\gamma\\\beta\cdot\tau\quad\beta\cdot\gamma\end{matrix}\right| \end{aligned}\]

3.\(nBSGS\)

\(n\)次詢問\(g^x\equiv y_i(mod\ p)\)

如果每次都做一遍\(BSGS\)\(n\sqrt{p}\)的,這也太慢了吧\kk,怎么辦呢。

注意我們\(BSGS\)的過程,設\(y_i=aK-b\),原式化為\((g^K)^a\equiv y_i\times g^b(mod\ p)\)

注意到左邊的不會改變,所以我們設\(K=\sqrt{\frac{p}{n}}\),於是\(a\)\(\frac{p}{K}=\sqrt{np}\)個,開始預處理出來,而\(b\)只有\(K=\sqrt{\frac{p}{n}}\)個,一共做\(n\)次,復雜度就變成了\(\sqrt{np}\)

4.\(O(n)-O(1)rmq\)

復雜度優秀寫起來也簡單QAQ

這里拿區間最大值來說。

考慮對序列分塊,塊大小為\(O(logn)\),所以一共有\(O(\frac{n}{logn})\)個塊。

首先考慮\(l,r\)不在同一個塊,顯然可以對整塊用st表查詢最大值,零散塊分別是一個前綴和后綴最大值,可以\(O(n)\)預處理出來。

然后\(l,r\)在同一個塊內的情況,考慮在每個塊維護一個遞減的單調棧,那么\([l,r]\)之間的最大值就是在加入\(r\)之后從底部到頂部第一個下標大於等於\(l\)的元素。

那么考慮壓位,每個點記錄下來加入這個點的元素后當前塊的單調棧形態,第\(i\)位二進制數表示當前塊的左端點\(+i\)這個元素存不存在,然后每次查詢相當於查詢\(r\)位置存的二進制數后綴\(0\)的個數,\(\underline{~~~~}builtin\underline{~~}ctz(x)\)可以返回\(x\)的二進制中后綴\(0\)的個數,於是答案下標就是\(l+\underline{~~~~}builtin\underline{~~}ctz(x>>l-1)\),左移是因為有了\(l\)個元素。

一般實現塊大小采用\(32\),壓到\(unsigned\)里。

這是由乃救爺爺的代碼:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define reg register
const int N = 2e7;
const int M = 7e5;
using namespace std;
namespace GenHelper
{
    unsigned z1,z2,z3,z4,b;
    unsigned rand_()
    {
    b=((z1<<6)^z1)>>13;
    z1=((z1&4294967294U)<<18)^b;
    b=((z2<<2)^z2)>>27;
    z2=((z2&4294967288U)<<2)^b;
    b=((z3<<13)^z3)>>21;
    z3=((z3&4294967280U)<<7)^b;
    b=((z4<<3)^z4)>>12;
    z4=((z4&4294967168U)<<13)^b;
    return (z1^z2^z3^z4);
    }
}
void srand(unsigned x)
{using namespace GenHelper;
z1=x; z2=(~x)^0x233333333U; z3=x^0x1234598766U; z4=(~x)+51;}
int read()
{
    using namespace GenHelper;
    int a=rand_()&32767;
    int b=rand_()&32767;
    return a*32768+b;
}
int n,m,s,a[N + 5],L,R,bln,pre[N + 5],suf[N + 5],mx[M + 5],stk[50],top,lg[M + 5],st[M + 5][21],idc;
unsigned bo[N + 5];
unsigned long long ans;
inline void prework()
{
	bln = ((n - 1) >> 5) + 1;
	for (reg int x = 1;x <= bln;x++)
	{
		L = ((x - 1) << 5) | 1,R = min(n,(x << 5));
		for (reg int i = L;i <= R;i++)
		{
			mx[x] = max(mx[x],a[i]);
			pre[i] = mx[x];
		}
		suf[R] = a[R];
		st[x][0] = mx[x];
		for (reg int i = R - 1;i >= L;i--)
			suf[i] = max(suf[i + 1],a[i]);
		top = 0;
		unsigned t = 0;
		for (reg int i = L;i <= R;i++)
		{
			while (top && a[stk[top]] < a[i])
			{
				t ^= 1 << stk[top] - L;
				top--;
			}
			t |= 1 << i - L;
			stk[++top] = i;
			bo[i] = t;
		}
	}
	lg[1] = 0;
	for (reg int i = 2;i <= M;i++)
		lg[i] = lg[i / 2] + 1;
	for (reg int i = 1;(1 << i) <= bln;i++)
		for (reg int j = 1;j + (1 << i - 1) - 1 <= bln;j++)
		{
			int l = st[j][i - 1],r = st[j + (1 << i - 1)][i - 1];
			st[j][i] = max(l,r);
		}
}
inline int qST(reg int l,reg int r)
{
	if (l > r)
		return 0;
	int len = lg[r - l + 1];
	return max(st[l][len],st[r - (1 << len) + 1][len]);
}
inline int query(reg int l,reg int r)
{
	int bll = ((l - 1) >> 5) + 1,blr = ((r - 1) >> 5) + 1;
	if (bll != blr)
	{
		int L = ((bll - 1) << 5) + 1;
		int ans = qST(bll + 1,blr - 1);
		ans = max(ans,suf[l]);
		ans = max(ans,pre[r]);
		return ans;
	}
	else
	{
		int pos = l + __builtin_ctz(bo[r] >> l - L);
		return a[pos];
	}
}
int main()
{
	scanf("%d%d%d",&n,&m,&s);
	srand(s);
	for (reg int i = 1;i <= n;i++)
		a[i] = read();
	prework();
	int l,r;
	while (m--)
	{
		l = read() % n + 1;r = read() % n + 1;
		if (l > r)
			swap(l,r);
		ans += query(l,r);
	}
	printf("%llu\n",ans);
	return 0;
}

5.\(O(nlogn)\)三維偏序?嗯

\(a_i<a_j,b_i<b_j,c_i<c_j\)的個數。

特殊要求:同一維元素互不相同,(有相同的好像也能做?/kk)

我們觀察一對\((i,j)\),他們存在的偏序關系要么是存在二維偏序要么是三維偏序。

於是我們把任意兩維偏序都拿出來做二維偏序,也就是對\((a,b),(a,c),(b,c)\)分別做二維偏序記錄和\(sm\),會發現一對\((i,j)\)如果是二維偏序那么會被統計一次,如果是三維偏序那么會被統計三次,每一對\((i,j)\)都作為二維偏序統計了一次的個數是\(n(n-1)/2\),然后只剩下兩倍的三維偏序數,於是我們可以得到三維偏序的數量\(=\frac{1}{2}(sm-n(n-1)/2)\)

6.三元環計數。

復雜度是\(m\sqrt{m}\),但是這個可以\(bitset\)優化啊啊啊,某次考試的時候竟然沒想到。

打上標記之后直接按位與一下就可以了,復雜度是\(O(\frac{n^3}{w})\)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM