OYXBH324
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # ZJUT4板子 ## 基础 ``` vector, 变长数组,倍增的思想 size() 返回元素个数 empty() 返回是否为空 clear() 清空 front()/back()返回第一个/最后一个数 push_back()插入一个元素 pop_back()删除最后一个元素 begin()/end() [] 支持比较运算<>=,按字典序 pair<int, int>存储一个二元组 first, 第一个元素 second, 第二个元素 支持比较运算,以first为第一关键字; 以second为第二关键字(字典序) string,字符串 size()/length() 返回字符串长度 empty() clear() substr(起始下标,(子串长度)) 返回子串 c_str() 返回字符串所在字符数组的起始地址 queue, 队列 size() empty() push() 向队尾插入一个元素 front() 返回队头元素 back() 返回队尾元素 pop() 弹出队头元素 priority_queue, 优先队列,默认是大根堆 size() empty() push() 插入一个元素 top() 返回堆顶元素 pop() 弹出堆顶元素 定义成小根堆的方式:priority_queue<int, vector<int>, greater<int>> q; stack, 栈 size() empty() push() 向栈顶插入一个元素 top() 返回栈顶元素 pop() 弹出栈顶元素 deque, 双端队列 size() empty() clear() front()/back() push_back()/pop_back() push_front()/pop_front() begin()/end() [] set, map, multiset, multimap, 基于平衡二叉树(红黑树),动态维护有序序列 size() empty() clear() begin()/end() ++, -- 返回前驱和后继,时间复杂度 O(logn) set/multiset 基于平衡二叉树(红黑树),动态维护有序数列 insert() 插入一个数 find() 查找一个数 count() 返回某一个数的个数 erase() (1) 输入是一个数x,删除所有x O(k + logn) (2) 输入一个迭代器,删除这个迭代器 lower_bound() && upper_bound() lower_bound(x) 返回大于等于x的最小的数的迭代器 upper_bound(x) 返回大于x的最小的数的迭代器 map/multimap 基于平衡二叉树(红黑树),动态维护有序数列 insert() 插入的数是一个pair erase() 输入的参数是pair或者迭代器 find() [] 注意multimap不支持此操作。 时间复杂度是 O(logn) lower_bound()/upper_bound() unordered_set, unordered_map, unordered_multiset, unordered_multimap, 哈希表 和上面类似,增删改查的时间复杂度是 O(1) 不支持 lower_bound()/upper_bound(), 迭代器的++,-- bitset, 压位 bitset<10000> s; ~, &, |, ^ >>, << ==, != [] count() 返回有多少个1 any() 判断是否至少有一个1 none() 判断是否全为0 set() 把所有位置成1 set(k, v) 将第k位变成v reset() 把所有位变成0 flip() 等价于~ flip(k) 把第k位取反 ``` ### mutiset简单记录: ``` struct cmp{ bool operator()(const ...&a,const ...&b){ return ....; } }; set<Elem,cmp>c; c.size();//返回大小 c.empty();//返回是否为空 count(elem);//返回elem元素的个数 find(elem);//返回元素值为elem的第一个元素,如果没有返回end() lower_bound(elem); upper_bound (elem); c.begin(); c.end(); c.insert(elem); distance(..,..)//返回两者之间的距离 c.erase(elem)//删除与elem相等的所有元素,返回被移除的元素个数。 c.erase(pos)//移除迭代器pos所指位置元素,无返回值。 c.clear()//移除所有元素,将容器清空 ``` ### bitset相关 ``` ///C++的 bitset 在 bitset 头文件中, ///它是一种类似数组的结构. /// 它的每一个元素只能是0或1. /// 每个元素仅用1bit空间。 ///常用构造 //用字符串构造时,字符串只能包含 '0' 或 '1' ,否则会抛出异常。 // //构造时,需在<>中表明bitset 的大小(即size)。 // //在进行有参构造时,若参数的二进制表示比bitset的size小,则在前面用0补充(如上面的栗子);若比bitsize大,参数为整数时取后面部分,参数为字符串时取前面部分(如下面栗子): bitset<4> bitset1; //无参构造,长度为4,默认每一位为0 bitset<8> bitset2(12); //长度为8,二进制保存,前面用0补充 string s = "100101"; bitset<10> bitset3(s); //长度为10,前面用0补充 char s2[] = "10101"; bitset<13> bitset4(s2); //长度为13,前面用0补充 ///可用操作符 bitset<4> foo (string("1001")); bitset<4> bar (string("0011")); cout << (foo^=bar) << endl; // 1010 (foo对bar按位异或后赋值给foo) cout << (foo&=bar) << endl; // 0010 (按位与后赋值给foo) cout << (foo|=bar) << endl; // 0011 (按位或后赋值给foo) cout << (foo<<=2) << endl; // 1100 (左移2位,低位补0,有自身赋值) cout << (foo>>=1) << endl; // 0110 (右移1位,高位补0,有自身赋值) cout << (~bar) << endl; // 1100 (按位取反) cout << (bar<<1) << endl; // 0110 (左移,不赋值) cout << (bar>>1) << endl; // 0001 (右移,不赋值) cout << (foo==bar) << endl; // false (0110==0011为false) cout << (foo!=bar) << endl; // true (0110!=0011为true) cout << (foo&bar) << endl; // 0010 (按位与,不赋值) cout << (foo|bar) << endl; // 0111 (按位或,不赋值) cout << (foo^bar) << endl; // 0101 (按位异或,不赋值) ///可用函数 bitset<8> foo ("10011011"); cout << foo.count() << endl; //5  (count函数用来求bitset中1的位数,foo中共有5个1 cout << foo.size() << endl; //8  (size函数用来求bitset的大小,一共有8位 cout << foo.test(0) << endl; //true  (test函数用来查下标处的元素是0还是1,并返回false或true,此处foo[0]为1,返回true //test访问比[]安全,因为会检查下标越界 cout << foo.test(2) << endl; //false  (同理,foo[2]为0,返回false cout << foo.any() << endl; //true  (any函数检查bitset中是否有1 cout << foo.none() << endl; //false  (none函数检查bitset中是否没有1 cout << foo.all() << endl; //false  (all函数检查bitset中是全部为1 ///可用函数2 bitset<8> foo ("10011011"); cout << foo.flip(2) << endl; //10011111  (flip函数传参数时,用于将参数位取反,本行代码将foo下标2处"反转",即0变1,1变0 cout << foo.flip() << endl; //01100000  (flip函数不指定参数时,将bitset每一位全部取反 cout << foo.set() << endl; //11111111  (set函数不指定参数时,将bitset的每一位全部置为1 cout << foo.set(3,0) << endl; //11110111  (set函数指定两位参数时,将第一参数位的元素置为第二参数的值,本行对foo的操作相当于foo[3]=0 cout << foo.set(3) << endl; //11111111  (set函数只有一个参数时,将参数下标处置为1 cout << foo.reset(4) << endl; //11101111  (reset函数传一个参数时将参数下标处置为0 cout << foo.reset() << endl; //00000000  (reset函数不传参数时将bitset的每一位全部置为0 ///类型转换 bitset<8> foo ("10011011"); string s = foo.to_string(); //将bitset转换成string类型 unsigned long a = foo.to_ulong(); //将bitset转换成unsigned long类型 unsigned long long b = foo.to_ullong(); //将bitset转换成unsigned long long类型 cout << s << endl; //10011011 cout << a << endl; //155 cout << b << endl; //155 ``` ### 二分相关 ``` ///整数二分板子 bool check(int x) {/* ... */} // 检查x是否满足某种性质 // 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用: int bsearch_1(int l, int r) { while (l < r) { int mid = l + r >> 1; if (check(mid)) r = mid; //mid满足条件,那么 else l = mid + 1; } return l; } // check()判断mid是否满足性质 // 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用: int bsearch_2(int l, int r) { while (l < r) { int mid = l + r + 1 >> 1; if (check(mid)) l = mid; //mid满足条件,边界在mid后面包括mid else r = mid - 1; //mid不满足条件,边界在mid前面 } return l; } ///浮点数二分 bool check(double x) {/* ... */} // 检查x是否满足某种性质 double bsearch_3(double l, double r) { const double eps = 1e-6; // eps 表示精度,取决于题目对精度的要求 while (r - l > eps) { double mid = (l + r) / 2; if (check(mid)) r = mid; else l = mid; } return l; } ///lower_bound( begin,end,num): // 从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字, // 找到返回该数字的地址,不存在则返回end。 // 通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。 ///upper_bound( begin,end,num): // 从数组的begin位置到end-1位置二分查找第一个大于num的数字, // 找到返回该数字的地址,不存在则返回end。 // 通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。 ///在从大到小的排序数组中,重载lower_bound()和upper_bound() ///lower_bound( begin,end,num,greater<type>() ): //从数组的begin位置到end-1位置二分查找第一个小于或等于num的数字 ///upper_bound( begin,end,num,greater<type>() ): // 从数组的begin位置到end-1位置二分查找第一个小于num的数字 ``` ## 搜索 ### A* ``` #include <algorithm> #include <cstdio> #include <cstring> #include <queue> using namespace std; const int maxn = 5010; const int maxm = 400010; const double inf = 2e9; int n, m, k, u, v, cur, h[maxn], nxt[maxm], p[maxm], cnt[maxn], ans; int cur1, h1[maxn], nxt1[maxm], p1[maxm]; double e, ww, w[maxm], f[maxn]; double w1[maxm]; bool tf[maxn]; void add_edge(int x, int y, double z) { //正向建图函数 cur++; nxt[cur] = h[x]; h[x] = cur; p[cur] = y; w[cur] = z; } void add_edge1(int x, int y, double z) { //反向建图函数 cur1++; nxt1[cur1] = h1[x]; h1[x] = cur1; p1[cur1] = y; w1[cur1] = z; } struct node { //使用A*时所需的结构体 int x; double v; bool operator<(node a) const { return v + f[x] > a.v + f[a.x]; } }; priority_queue<node> q; struct node2 { //计算t到所有结点最短路时所需的结构体 int x; double v; bool operator<(node2 a) const { return v > a.v; } } x; priority_queue<node2> Q; int main() { scanf("%d%d%lf", &n, &m, &e); while (m--) { scanf("%d%d%lf", &u, &v, &ww); add_edge(u, v, ww); //正向建图 add_edge1(v, u, ww); //反向建图 } for (int i = 1; i < n; i++) f[i] = inf; Q.push({n, 0}); while (!Q.empty()) { //计算t到所有结点的最短路 x = Q.top(); Q.pop(); if (tf[x.x]) continue; tf[x.x] = true; f[x.x] = x.v; for (int j = h1[x.x]; j; j = nxt1[j]) Q.push({p1[j], x.v + w1[j]}); } k = (int)e / f[1]; q.push({1, 0}); while (!q.empty()) { //使用A*算法 node x = q.top(); q.pop(); cnt[x.x]++; if (x.x == n) { e -= x.v; if (e < 0) { printf("%d\n", ans); return 0; } ans++; } for (int j = h[x.x]; j; j = nxt[j]) if (cnt[p[j]] <= k && x.v + w[j] <= e) q.push({p[j], x.v + w[j]}); } printf("%d\n", ans); return 0; } ``` ## 图论 ### Kruscal ``` const int maxn=1e5+5; int n,m; int fa[maxn]; struct side{ int x,y,l; }g[maxn]; bool cmp(side x,side y){ return x.l<y.l; } void init(){ for (int i=1; i<=n; i++){ fa[i]=i; } } int find(int x){ if (fa[x]==x) return fa[x]; else return fa[x]=find(fa[x]); } int kruscal(){ sort(g+1,g+1+m,cmp); int tot=0,res=0; for (int i=1; i<=m; i++){ int u=find(g[i].x),v=find(g[i].y); if (u!=v){ res=max(res,g[i].l); fa[u]=v; tot++; } if (tot==n-1) break; } return res; } ``` ### Prim ``` const int maxn=1e5+5; const int INF=0X3f3f3f3f; int n,m; int dis[maxn]; bool flag[maxn]; int g[305][305]; struct node{ int x,l; friend bool operator < (struct node a,struct node b){ return a.l>b.l; } }; int prim(){ priority_queue<node> q; int res=0; for (int i=1; i<=n; i++) dis[i]=INF; dis[1]=0; q.push({1,0}); memset(flag,0,sizeof(flag)); while (!q.empty()){ node x=q.top(); q.pop(); int now=x.x; if (flag[now]==1) continue; flag[now]=1; res=max(res,dis[now]); for (int i=1; i<=n; i++){ if (!flag[i] && dis[i]>g[now][i]){ dis[i]=g[now][i]; q.push({i,dis[i]}); } } } return res; } ``` ### dijkstra ``` const int N = 2510; const int M = 15000; typedef pair<int, int> P; struct DIJ{ struct edge{ int u, v; int cost; int next; }es[M]; int head[N], pre[N]; int d[N]; int V, tot; void init(int n) { V = n; tot = 0; for (int i = 0; i <= V; i++) head[i] = pre[i] = -1; } void add_edge(int u, int v, int cost) { es[tot].u = u; es[tot].v = v; es[tot].cost = cost; es[tot].next = head[u]; head[u] = tot++; } void dijkstra(int s, int d[]) { priority_queue<P, vector<P>, greater<P> > pq; for (int i = 0; i <= V; i++) d[i] = INF; d[s] = 0; pq.push(P(0, s)); while (pq.size()) { P p = pq.top(); pq.pop(); int v = p.second; if (d[v] < p.first) continue; for (int i = head[v]; i != -1; i = es[i].next) { int from = es[i].u; int to = es[i].v; int cost = es[i].cost; if (d[to] > d[from] + cost) { d[to] = d[from] + cost; pq.push(P(d[to], to)); pre[to] = from; } } } } vector<int> get_path(int t) { vector<int> path; for (; t != -1; t = pre[t]) path.push_back(t); reverse(path.begin(), path.end()); return path; } }G; ``` ### bellman判负环 ``` bool Bellman_Ford() { for (int i = 0; i < n; i++) { bool jud = false; for (int j = 1; j <= n; j++) for (int k = h[j]; ~k; k = nxt[k]) if (dist[j] > dist[p[k]] + w[k]) dist[j] = dist[p[k]] + w[k], jud = true; if (!jud) break; } for (int i = 1; i <= n; i++) for (int j = h[i]; ~j; j = nxt[j]) if (dist[i] > dist[p[j]] + w[j]) return false; return true; } ``` ### K短路 ``` #include <algorithm> #include <cstdio> #include <cstring> #include <queue> using namespace std; const int maxn = 200010; int n, m, s, t, k, x, y, ww, cnt, fa[maxn]; struct Edge { int cur, h[maxn], nxt[maxn], p[maxn], w[maxn]; void add_edge(int x, int y, int z) { cur++; nxt[cur] = h[x]; h[x] = cur; p[cur] = y; w[cur] = z; } } e1, e2; int dist[maxn]; bool tf[maxn], vis[maxn], ontree[maxn]; struct node { int x, v; node* operator=(node a) { x = a.x; v = a.v; return this; } bool operator<(node a) const { return v > a.v; } } a; priority_queue<node> Q; void dfs(int x) { vis[x] = true; for (int j = e2.h[x]; j; j = e2.nxt[j]) if (!vis[e2.p[j]]) if (dist[e2.p[j]] == dist[x] + e2.w[j]) fa[e2.p[j]] = x, ontree[j] = true, dfs(e2.p[j]); } struct LeftistTree { int cnt, rt[maxn], lc[maxn * 20], rc[maxn * 20], dist[maxn * 20]; node v[maxn * 20]; LeftistTree() { dist[0] = -1; } int newnode(node w) { cnt++; v[cnt] = w; return cnt; } int merge(int x, int y) { if (!x || !y) return x + y; if (v[x] < v[y]) swap(x, y); int p = ++cnt; lc[p] = lc[x]; v[p] = v[x]; rc[p] = merge(rc[x], y); if (dist[lc[p]] < dist[rc[p]]) swap(lc[p], rc[p]); dist[p] = dist[rc[p]] + 1; return p; } } st; void dfs2(int x) { vis[x] = true; if (fa[x]) st.rt[x] = st.merge(st.rt[x], st.rt[fa[x]]); for (int j = e2.h[x]; j; j = e2.nxt[j]) if (fa[e2.p[j]] == x && !vis[e2.p[j]]) dfs2(e2.p[j]); } int main() { scanf("%d%d%d%d%d", &n, &m, &s, &t, &k); for (int i = 1; i <= m; i++) scanf("%d%d%d", &x, &y, &ww), e1.add_edge(x, y, ww), e2.add_edge(y, x, ww); Q.push({t, 0}); while (!Q.empty()) { a = Q.top(); Q.pop(); if (tf[a.x]) continue; tf[a.x] = true; dist[a.x] = a.v; for (int j = e2.h[a.x]; j; j = e2.nxt[j]) Q.push({e2.p[j], a.v + e2.w[j]}); } if (k == 1) { if (tf[s]) printf("%d\n", dist[s]); else printf("-1\n"); return 0; } dfs(t); for (int i = 1; i <= n; i++) if (tf[i]) for (int j = e1.h[i]; j; j = e1.nxt[j]) if (!ontree[j]) if (tf[e1.p[j]]) st.rt[i] = st.merge( st.rt[i], st.newnode({e1.p[j], dist[e1.p[j]] + e1.w[j] - dist[i]})); for (int i = 1; i <= n; i++) vis[i] = false; dfs2(t); if (st.rt[s]) Q.push({st.rt[s], dist[s] + st.v[st.rt[s]].v}); while (!Q.empty()) { a = Q.top(); Q.pop(); cnt++; if (cnt == k - 1) { printf("%d\n", a.v); return 0; } if (st.lc[a.x]) Q.push({st.lc[a.x], a.v - st.v[a.x].v + st.v[st.lc[a.x]].v}); if (st.rc[a.x]) Q.push({st.rc[a.x], a.v - st.v[a.x].v + st.v[st.rc[a.x]].v}); x = st.rt[st.v[a.x].x]; if (x) Q.push({x, a.v + st.v[x].v}); } printf("-1\n"); return 0; } ``` ### 点分治 ``` #include<bits/stdc++.h> using namespace std; const int N = 1e5 + 5,M=1e7+6; int n, m, u, v, w,Q[N],ans[N]; int root,cnt,S,temp[N],dis[N],max_childrenpart[N],size[N]; bool use[N],judge[M]; // judge i 与当前根距离为i的路径是否存在 // use 表示该点有没有被遍历过 struct node { int link, w; }; vector <node> f[N]; queue <int> q; void find_root(int x,int fa)// 板子 { max_childrenpart[x] = 0; size[x] = 1; for(auto i:f[x]) { if(i.link==fa||use[i.link]) continue; find_root(i.link, x); size[x] += size[i.link]; max_childrenpart[x] = max(size[i.link],max_childrenpart[x]); } max_childrenpart[x] = max(max_childrenpart[x],S-max_childrenpart[x]); if(max_childrenpart[x]<max_childrenpart[root]) root = x; } void get_dis(int x,int fa) //板子 { temp[++cnt] = dis[x]; for(auto i:f[x]) { if(use[i.link]||i.link==fa) continue; dis[i.link] = dis[x] + i.w; get_dis(i.link, x); } } void solve(int root)//不同题不同 { while(!q.empty()) q.pop(); for(auto i:f[root]) { if(use[i.link]) continue; cnt = 0; // 所有点距离的计数器 dis[i.link] = i.w; // 第一次要初始化一下 get_dis(i.link,root);//计算距离root节点的距离 for (int j = 1; j <= cnt; j++) { for (int k = 1; k <= m;k++)//这里肯定是可以优化的 因为没优化re了(judge爆了 { if(Q[k]>=temp[j])//询问路径大于 遍历的节点到根的距离说明可能存在 { if(judge[Q[k]-temp[j]]) { // 如果非当前子树存在到根距离为Q[k]-dis[j]的点 // 说明答案存在 ans[k] = 1; } } } } for (int j = 1; j <= cnt;j++) { q.push(temp[j]); judge[temp[j]] = 1; } } while(!q.empty()) { judge[q.front()] = 0;// 撤回 q.pop(); } } void divided(int x)//板子 { use[x] = 1; judge[0] = 1; solve(x); for(auto i:f[x]) { if(use[i.link]) continue; // 初始化根节点 root = 0; max_childrenpart=n+1; //反正一定要大不然可能不会更新,之前max_childrenpart=size[i.link]是错的 S= size[i.link]; find_root(i.link,0); divided(root); } } int main() { scanf("%d %d", &n, &m); for (int i = 1; i < n;i++) { scanf("%d %d %d", &u, &v, &w); f[u].emplace_back((node){v, w}); f[v].emplace_back((node){u, w}); } for (int i = 1; i <= m;i++) scanf("%d", &Q[i]); root = 0; S=max_childrenpart[root] = n; find_root(1,0);//先找到重心 divided(root);//重心开始第一次分治 for (int i = 1; i <= m;i++) { if(ans[i]) printf("AYE\n"); else printf("NAY\n"); } return 0; } ``` ### 树上启发合并 ``` #include <bits/stdc++.h> using namespace std; const int N = 2e5 + 5; int n; // g[u]: 存储与 u 相邻的结点 vector<int> g[N]; // sz: 子树大小 // big: 重儿子 // col: 结点颜色 // L[u]: 结点 u 的 DFS 序 // R[u]: 结点 u 子树中结点的 DFS 序的最大值 // Node[i]: DFS 序为 i 的结点 // ans: 存答案 // cnt[i]: 颜色为 i 的结点个数 // totColor: 目前出现过的颜色个数 int sz[N], big[N], col[N], L[N], R[N], Node[N], totdfn; int ans[N], cnt[N], totColor; void add(int u) { if (cnt[col[u]] == 0) ++totColor; cnt[col[u]]++; } void del(int u) { cnt[col[u]]--; if (cnt[col[u]] == 0) --totColor; } int getAns() { return totColor; } void dfs0(int u, int p) { L[u] = ++totdfn; Node[totdfn] = u; sz[u] = 1; for (int v : g[u]) if (v != p) { dfs0(v, u); sz[u] += sz[v]; if (!big[u] || sz[big[u]] < sz[v]) big[u] = v; } R[u] = totdfn; } void dfs1(int u, int p, bool keep) { // 计算轻儿子的答案 for (int v : g[u]) if (v != p && v != big[u]) { dfs1(v, u, false); } // 计算重儿子答案并保留计算过程中的数据(用于继承) if (big[u]) { dfs1(big[u], u, true); } for (int v : g[u]) if (v != p && v != big[u]) { // 子树结点的 DFS 序构成一段连续区间,可以直接遍历 for (int i = L[v]; i <= R[v]; i++) { add(Node[i]); } } add(u); ans[u] = getAns(); if (keep == false) { for (int i = L[u]; i <= R[u]; i++) { del(Node[i]); } } } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &col[i]); for (int i = 1; i < n; i++) { int u, v; scanf("%d%d", &u, &v); g[u].push_back(v); g[v].push_back(u); } dfs0(1, 0); dfs1(1, 0, false); for (int i = 1; i <= n; i++) printf("%d%c", ans[i], " \n"[i == n]); return 0; } ``` ### 虚树 ``` inline bool cmp(const int x, const int y) { return id[x] < id[y]; } void build() { sort(h + 1, h + k + 1, cmp); sta[top = 1] = 1, g.sz = 0, g.head[1] = -1; // 1 号节点入栈,清空 1 号节点对应的邻接表,设置邻接表边数为 1 for (int i = 1, l; i <= k; ++i) if (h[i] != 1) { //如果 1 号节点是关键节点就不要重复添加 l = lca(h[i], sta[top]); //计算当前节点与栈顶节点的 LCA if (l != sta[top]) { //如果 LCA 和栈顶元素不同,则说明当前节点不再当前栈所存的链上 while (id[l] < id[sta[top - 1]]) //当次大节点的 Dfs 序大于 LCA 的 Dfs 序 g.push(sta[top - 1], sta[top]), top--; //把与当前节点所在的链不重合的链连接掉并且弹出 if (id[l] > id[sta[top - 1]]) //如果 LCA 不等于次大节点(这里的大于其实和不等于没有区别) g.head[l] = -1, g.push(l, sta[top]), sta[top] = l; //说明 LCA 是第一次入栈,清空其邻接表,连边后弹出栈顶元素,并将 LCA 入栈 else g.push(l, sta[top--]); //说明 LCA 就是次大节点,直接弹出栈顶元素 } g.head[h[i]] = -1, sta[++top] = h[i]; //当前节点必然是第一次入栈,清空邻接表并入栈 } for (int i = 1; i < top; ++i) g.push(sta[i], sta[i + 1]); //剩余的最后一条链连接一下 return; } ``` ### 二分图 #### 匈牙利算法 ``` int n1, n2; // n1表示第一个集合中的点数,n2表示第二个集合中的点数 int h[N], e[M], ne[M], idx; // 邻接表存储所有边,匈牙利算法中只会用到从第一个集合指向第二个集合的边,所以这里只用存一个方向的边 int match[N]; // 存储第二个集合中的每个点当前匹配的第一个集合中的点是哪个 bool st[N]; // 表示第二个集合中的每个点是否已经被遍历过 bool find(int x) { for (int i = h[x]; i != -1; i = ne[i]) { int j = e[i]; if (!st[j]) { st[j] = true; if (match[j] == 0 || find(match[j])) { match[j] = x; return true; } } } return false; } // 求最大匹配数,依次枚举第一个集合中的每个点能否匹配第二个集合中的点 int res = 0; for (int i = 1; i <= n1; i ++ ) { memset(st, false, sizeof st); if (find(i)) res ++ ; } ``` ### 割点 ``` #include <bits/stdc++.h> using namespace std; int n, m; // n:点数 m:边数 int num[100001], low[100001], inde, res; // num:记录每个点的时间戳 // low:能不经过父亲到达最小的编号,inde:时间戳,res:答案数量 bool vis[100001], flag[100001]; // flag: 答案 vis:标记是否重复 vector<int> edge[100001]; // 存图用的 void Tarjan(int u, int father) { // u 当前点的编号,father 自己爸爸的编号 vis[u] = true; // 标记 low[u] = num[u] = ++inde; // 打上时间戳 int child = 0; // 每一个点儿子数量 for (auto v : edge[u]) { // 访问这个点的所有邻居 (C++11) if (!vis[v]) { child++; // 多了一个儿子 Tarjan(v, u); // 继续 low[u] = min(low[u], low[v]); // 更新能到的最小节点编号 if (father != u && low[v] >= num[u] && !flag [u]) // 主要代码 // 如果不是自己,且不通过父亲返回的最小点符合割点的要求,并且没有被标记过 // 要求即为:删了父亲连不上去了,即为最多连到父亲 { flag[u] = true; res++; // 记录答案 } } else if (v != father) low[u] = min(low[u], num[v]); // 如果这个点不是自己,更新能到的最小节点编号 } if (father == u && child >= 2 && !flag[u]) { // 主要代码,自己的话需要 2 个儿子才可以 flag[u] = true; res++; // 记录答案 } } int main() { cin >> n >> m; // 读入数据 for (int i = 1; i <= m; i++) { // 注意点是从 1 开始的 int x, y; cin >> x >> y; edge[x].push_back(y); edge[y].push_back(x); } // 使用 vector 存图 for (int i = 1; i <= n; i++) // 因为 Tarjan 图不一定连通 if (!vis[i]) { inde = 0; // 时间戳初始为 0 Tarjan(i, i); // 从第 i 个点开始,父亲为自己 } cout << res << endl; for (int i = 1; i <= n; i++) if (flag[i]) cout << i << " "; // 输出结果 return 0; } ``` ### 割边 ``` int low[MAXN], dfn[MAXN], iscut[MAXN], dfs_clock; bool isbridge[MAXN]; vector<int> G[MAXN]; int cnt_bridge; int father[MAXN]; void tarjan(int u, int fa) { father[u] = fa; low[u] = dfn[u] = ++dfs_clock; for (int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if (!dfn[v]) { tarjan(v, u); low[u] = min(low[u], low[v]); if (low[v] > dfn[u]) { isbridge[v] = true; ++cnt_bridge; } } else if (dfn[v] < dfn[u] && v != fa) { low[u] = min(low[u], dfn[v]); } } } ``` ### tarjan ``` int dfn[N], low[N], dfncnt, s[N], in_stack[N], tp; int scc[N], sc; // 结点 i 所在 scc 的编号 int sz[N]; // 强连通 i 的大小 void tarjan(int u) { low[u] = dfn[u] = ++dfncnt, s[++tp] = u, in_stack[u] = 1; for (int i = h[u]; i; i = e[i].nex) { const int &v = e[i].t; if (!dfn[v]) { tarjan(v); low[u] = min(low[u], low[v]); } else if (in_stack[v]) { low[u] = min(low[u], dfn[v]); } } if (dfn[u] == low[u]) { ++sc; while (s[tp] != u) { scc[s[tp]] = sc; sz[sc]++; in_stack[s[tp]] = 0; --tp; } scc[s[tp]] = sc; sz[sc]++; in_stack[s[tp]] = 0; --tp; } } ``` ## 网络流 ### 最大流 #### Dinic ``` #define maxn 250 #define INF 0x3f3f3f3f struct Edge { int from, to, cap, flow; Edge(int u, int v, int c, int f) : from(u), to(v), cap(c), flow(f) {} }; struct Dinic { int n, m, s, t; vector<Edge> edges; vector<int> G[maxn]; int d[maxn], cur[maxn]; bool vis[maxn]; void init(int n) { for (int i = 0; i < n; i++) G[i].clear(); edges.clear(); } void AddEdge(int from, int to, int cap) { edges.push_back(Edge(from, to, cap, 0)); edges.push_back(Edge(to, from, 0, 0)); m = edges.size(); G[from].push_back(m - 2); G[to].push_back(m - 1); } bool BFS() { memset(vis, 0, sizeof(vis)); queue<int> Q; Q.push(s); d[s] = 0; vis[s] = 1; while (!Q.empty()) { int x = Q.front(); Q.pop(); for (int i = 0; i < G[x].size(); i++) { Edge& e = edges[G[x][i]]; if (!vis[e.to] && e.cap > e.flow) { vis[e.to] = 1; d[e.to] = d[x] + 1; Q.push(e.to); } } } return vis[t]; } int DFS(int x, int a) { if (x == t || a == 0) return a; int flow = 0, f; for (int& i = cur[x]; i < G[x].size(); i++) { Edge& e = edges[G[x][i]]; if (d[x] + 1 == d[e.to] && (f = DFS(e.to, min(a, e.cap - e.flow))) > 0) { e.flow += f; edges[G[x][i] ^ 1].flow -= f; flow += f; a -= f; if (a == 0) break; } } return flow; } int Maxflow(int s, int t) { this->s = s; this->t = t; int flow = 0; while (BFS()) { memset(cur, 0, sizeof(cur)); flow += DFS(s, INF); } return flow; } }; ``` #### ISAP ``` struct Edge { int from, to, cap, flow; Edge(int u, int v, int c, int f) : from(u), to(v), cap(c), flow(f) {} }; bool operator<(const Edge& a, const Edge& b) { return a.from < b.from || (a.from == b.from && a.to < b.to); } struct ISAP { int n, m, s, t; vector<Edge> edges; vector<int> G[maxn]; bool vis[maxn]; int d[maxn]; int cur[maxn]; int p[maxn]; int num[maxn]; void AddEdge(int from, int to, int cap) { edges.push_back(Edge(from, to, cap, 0)); edges.push_back(Edge(to, from, 0, 0)); m = edges.size(); G[from].push_back(m - 2); G[to].push_back(m - 1); } bool BFS() { memset(vis, 0, sizeof(vis)); queue<int> Q; Q.push(t); vis[t] = 1; d[t] = 0; while (!Q.empty()) { int x = Q.front(); Q.pop(); for (int i = 0; i < G[x].size(); i++) { Edge& e = edges[G[x][i] ^ 1]; if (!vis[e.from] && e.cap > e.flow) { vis[e.from] = 1; d[e.from] = d[x] + 1; Q.push(e.from); } } } return vis[s]; } void init(int n) { this->n = n; for (int i = 0; i < n; i++) G[i].clear(); edges.clear(); } int Augment() { int x = t, a = INF; while (x != s) { Edge& e = edges[p[x]]; a = min(a, e.cap - e.flow); x = edges[p[x]].from; } x = t; while (x != s) { edges[p[x]].flow += a; edges[p[x] ^ 1].flow -= a; x = edges[p[x]].from; } return a; } int Maxflow(int s, int t) { this->s = s; this->t = t; int flow = 0; BFS(); memset(num, 0, sizeof(num)); for (int i = 0; i < n; i++) num[d[i]]++; int x = s; memset(cur, 0, sizeof(cur)); while (d[s] < n) { if (x == t) { flow += Augment(); x = s; } int ok = 0; for (int i = cur[x]; i < G[x].size(); i++) { Edge& e = edges[G[x][i]]; if (e.cap > e.flow && d[x] == d[e.to] + 1) { ok = 1; p[e.to] = G[x][i]; cur[x] = i; x = e.to; break; } } if (!ok) { int m = n - 1; for (int i = 0; i < G[x].size(); i++) { Edge& e = edges[G[x][i]]; if (e.cap > e.flow) m = min(m, d[e.to]); } if (--num[d[x]] == 0) break; num[d[x] = m + 1]++; cur[x] = 0; if (x != s) x = edges[p[x]].from; } } return flow; } }; ``` ### 费用流 ``` #include <algorithm> #include <cstdio> #include <cstring> #include <queue> const int N = 5e3 + 5, M = 1e5 + 5; const int INF = 0x3f3f3f3f; int n, m, tot = 1, lnk[N], cur[N], ter[M], nxt[M], cap[M], cost[M], dis[N], ret; bool vis[N]; void add(int u, int v, int w, int c) { ter[++tot] = v, nxt[tot] = lnk[u], lnk[u] = tot, cap[tot] = w, cost[tot] = c; } void addedge(int u, int v, int w, int c) { add(u, v, w, c), add(v, u, 0, -c); } bool spfa(int s, int t) { memset(dis, 0x3f, sizeof(dis)); memcpy(cur, lnk, sizeof(lnk)); std::queue<int> q; q.push(s), dis[s] = 0, vis[s] = 1; while (!q.empty()) { int u = q.front(); q.pop(), vis[u] = 0; for (int i = lnk[u]; i; i = nxt[i]) { int v = ter[i]; if (cap[i] && dis[v] > dis[u] + cost[i]) { dis[v] = dis[u] + cost[i]; if (!vis[v]) q.push(v), vis[v] = 1; } } } return dis[t] != INF; } int dfs(int u, int t, int flow) { if (u == t) return flow; vis[u] = 1; int ans = 0; for (int &i = cur[u]; i && ans < flow; i = nxt[i]) { int v = ter[i]; if (!vis[v] && cap[i] && dis[v] == dis[u] + cost[i]) { int x = dfs(v, t, std::min(cap[i], flow - ans)); if (x) ret += x * cost[i], cap[i] -= x, cap[i ^ 1] += x, ans += x; } } vis[u] = 0; return ans; } int mcmf(int s, int t) { int ans = 0; while (spfa(s, t)) { int x; while ((x = dfs(s, t, INF))) ans += x; } return ans; } int main() { int s, t; scanf("%d%d%d%d", &n, &m, &s, &t); while (m--) { int u, v, w, c; scanf("%d%d%d%d", &u, &v, &w, &c); addedge(u, v, w, c); } int ans = mcmf(s, t); printf("%d %d\n", ans, ret); return 0; } ``` ### 上下界网络流 ![](https://i.imgur.com/PPs1AtM.png) ![](https://i.imgur.com/KxNvkHS.png) ![](https://i.imgur.com/Fg7F2dx.png) ![](https://i.imgur.com/tjp2dSq.png) ``` #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=300,maxm=100000; struct edge{ int to,next,w,num; }lst[maxm];int len=0,first[maxn],_first[maxn]; void addedge(int a,int b,int w,int num){ lst[len].num=num; lst[len].to=b;lst[len].next=first[a];lst[len].w=w;first[a]=len++; lst[len].num=num; lst[len].to=a;lst[len].next=first[b];lst[len].w=0;first[b]=len++; } int vis[maxn],dis[maxn],q[maxn],head,tail,s,t,T; bool bfs(){ vis[s]=++T;dis[s]=1;head=tail=0;q[tail++]=s; while(head!=tail){ int x=q[head++]; for(int pt=first[x];pt!=-1;pt=lst[pt].next){ if(lst[pt].w&&vis[lst[pt].to]!=T){ vis[lst[pt].to]=T;dis[lst[pt].to]=dis[x]+1;q[tail++]=lst[pt].to; } } } if(vis[t]==T)memcpy(_first,first,sizeof(first)); return vis[t]==T; } int dfs(int x,int lim){ if(x==t){ return lim; } int flow=0,a; for(int pt=_first[x];pt!=-1;pt=lst[pt].next){ _first[x]=pt; if(lst[pt].w&&dis[lst[pt].to]==dis[x]+1&&(a=dfs(lst[pt].to,min(lst[pt].w,lim-flow)))){ lst[pt].w-=a;lst[pt^1].w+=a;flow+=a; if(flow==lim)return flow; } } return flow; } int dinic(){ int ans=0,x; while(bfs()) while(x=dfs(s,0x7f7f7f7f))ans+=x; return ans; } int low[maxm],ans[maxm]; int totflow[maxn],n,m; void work(){ memset(totflow,0,sizeof(totflow)); memset(first,-1,sizeof(first));len=0; scanf("%d%d",&n,&m); int u,v,b; s=0;t=n+1; for(int i=1;i<=m;++i){ scanf("%d%d%d%d",&u,&v,&low[i],&b); addedge(u,v,b-low[i],i);totflow[u]-=low[i];totflow[v]+=low[i]; } int sum=0; for(int i=1;i<=n;++i){ if(totflow[i]<0){ addedge(i,t,-totflow[i],0); }else{ sum+=totflow[i]; addedge(s,i,totflow[i],0); } } if(dinic()==sum){ puts("YES"); for(int i=1;i<=n;++i){ for(int pt=first[i];pt!=-1;pt=lst[pt].next){ if(lst[pt].num==0||pt%2==0)continue; ans[lst[pt].num]=lst[pt].w+low[lst[pt].num]; } } for(int i=1;i<=m;++i)printf("%d\n",ans[i]); }else puts("NO"); } int main(){ int tests;scanf("%d",&tests); while(tests--){ work();if(tests)printf("\n"); } return 0; } ``` ## 数据结构 ### 关系并查集 ``` void give (int n) { for (int i=1;i<=n;i++) { father[i]=i; relat[i]=0; //初始关系都为0 } } int find(int x) { if (x==father[x]) return father[x]; int t=find(father[x]); relat[x]=(relat[x]+relat[father[x]])%3; //在寻找祖先节点的过程中也要更新关系 father[x]=t; return father[x]; } ``` ### st表 ``` void pre(){ //准备工作,初始化 Logn[1] = 0; Logn[2] = 1; for (int i = 3; i < maxn; i++){ Logn[i] = Logn[i / 2] + 1; } } for (int i = 1; i <= n; i++) f[i][0] = read(); pre(); for (int j = 1; j <= logn; j++) for (int i = 1; i + (1 << j) - 1 <= n; i++) f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]); // ST表具体实现 for (int i = 1; i <= m; i++){ int x = read(), y = read(); int s = Logn[y - x + 1]; printf("%d\n", max(f[x][s], f[y - (1 << s) + 1][s])); } ``` ### 树状数组 ``` int t1[MAXN], t2[MAXN], n; inline int lowbit(int x) { return x & (-x); } void add(int k, int v) { int v1 = k * v; while (k <= n) { t1[k] += v, t2[k] += v1; k += lowbit(k); } } int getsum(int *t, int k) { int ret = 0; while (k) { ret += t[k]; k -= lowbit(k); } return ret; } void add1(int l, int r, int v) { add(l, v), add(r + 1, -v); // 将区间加差分为两个前缀加 } long long getsum1(int l, int r) { return (r + 1ll) * getsum(t1, r) - 1ll * l * getsum(t1, l - 1) - (getsum(t2, r) - getsum(t2, l - 1)); } // C++ Version // 时间戳优化 int tag[MAXN], t[MAXN], Tag; void reset() { ++Tag; } void add(int k, int v) { while (k <= n) { if (tag[k] != Tag) t[k] = 0; t[k] += v, tag[k] = Tag; k += lowbit(k); } } int getsum(int k) { int ret = 0; while (k) { if (tag[k] == Tag) ret += t[k]; k -= lowbit(k); } return ret; } //二维 #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> #include <iostream> using namespace std; typedef long long ll; ll read(){ char c; bool op = 0; while((c = getchar()) < '0' || c > '9') if(c == '-') op = 1; ll res = c - '0'; while((c = getchar()) >= '0' && c <= '9') res = res * 10 + c - '0'; return op ? -res : res; } const int N = 2050; ll n, m, Q; ll t1[N][N], t2[N][N], t3[N][N], t4[N][N]; void add(ll x, ll y, ll z){ for(int X = x; X <= n; X += X & -X) for(int Y = y; Y <= m; Y += Y & -Y){ t1[X][Y] += z; t2[X][Y] += z * x; t3[X][Y] += z * y; t4[X][Y] += z * x * y; } } void range_add(ll xa, ll ya, ll xb, ll yb, ll z){ //(xa, ya) 到 (xb, yb) 的矩形 add(xa, ya, z); add(xa, yb + 1, -z); add(xb + 1, ya, -z); add(xb + 1, yb + 1, z); } ll ask(ll x, ll y){ ll res = 0; for(int i = x; i; i -= i & -i) for(int j = y; j; j -= j & -j) res += (x + 1) * (y + 1) * t1[i][j] - (y + 1) * t2[i][j] - (x + 1) * t3[i][j] + t4[i][j]; return res; } ll range_ask(ll xa, ll ya, ll xb, ll yb){ return ask(xb, yb) - ask(xb, ya - 1) - ask(xa - 1, yb) + ask(xa - 1, ya - 1); } int main(){ n = read(), m = read(); ll c; while(scanf("%lld",&c)!=EOF){ if (c==1){ ll ya = read(), xa = read(), yb = read(), xb = read(), a = read(); range_add(xa, ya, xb, yb, a); } else { ll ya = read(), xa = read(), yb = read(), xb = read(); printf("%lld\n",range_ask(xa,ya,xb,yb)); } } return 0; } ``` ### 线段树(区间修改单点修改区间查询) ``` const int maxn=1e5+5; struct seg_tree{ #define p2 (p<<1) #define p3 ((p<<1)|1) ll tr[maxn<<2],lz[maxn<<2],a[maxn]; /* *初始化* s->需维护数组 n数组长度 */ void init(ll *s,ll n){ for (int i=1; i<=n; i++){ a[i]=s[i]; } build(1,n,1); } /* *建树* l->1 r->整个区间长度 p->树下标,初始赋1 */ void build(int l,int r,int p){ if (l==r){ tr[p]=a[l]; lz[p]=0; return ; } int mid=l+r>>1; build(l,mid,p2); build(mid+1,r,p3); tr[p]=tr[p2]+tr[p3]; } /* *单点修改* l->1 r->整个区间长度 k->要修改的位置 p->树下标,初始赋1 v->修改值 */ inline void change(int l,int r,int k,int p,ll v){ if (l==r){ tr[p]=v; lz[p]=0; return; } int mid=l+r>>1; lz[p2]+=lz[p]; lz[p3]+=lz[p]; lz[p]=0; if (k<=mid) change(l,mid,k,p2,v); else change(mid+1,r,k,p3,v); tr[p]=tr[p2]+lz[p2]*(mid-l+1)+tr[p3]+lz[p3]*(r-mid); } /* *区间修改* l->1 r->整个区间长度 le->区间修改左区间 ri->区间修改右区间 p->树下标,初始赋1 v->修改值 */ inline void update(int l,int r,int le,int ri,int p,ll v){ if (l==le && r==ri){ lz[p]+=v; return ; } lz[p2]+=lz[p]; lz[p3]+=lz[p]; lz[p]=0; int mid=l+r>>1; if (le>mid) update(mid+1,r,le,ri,p3,v); else if (ri<=mid) update(l,mid,le,ri,p2,v); else update(l,mid,le,mid,p2,v),update(mid+1,r,mid+1,ri,p3,v); tr[p]=tr[p2]+lz[p2]*(mid-l+1)+tr[p3]+lz[p3]*(r-mid); } /* *区间查询* l->1 r->整个区间长度 le->区间查询左区间 ri->区间查询右区间 p->树下标,初始赋1 */ inline ll query(int l,int r,int le,int ri,int p){ if (l==le && r==ri){ return tr[p]+lz[p]*(r-l+1); } int mid=l+r>>1; ll ret; lz[p2]+=lz[p]; lz[p3]+=lz[p]; lz[p]=0; if (ri<=mid) ret=query(l,mid,le,ri,p2); else if(le>mid) ret=query(mid+1,r,le,ri,p3); else ret=query(l,mid,le,mid,p2)+query(mid+1,r,mid+1,ri,p3); tr[p]=tr[p2]+lz[p2]*(mid-l+1)+tr[p3]+lz[p3]*(r-mid); return ret; } }segt; ``` ### 区间历史最值 ![](https://i.imgur.com/hfbQf7d.png) ![](https://i.imgur.com/rBXoGTw.png) ![](https://i.imgur.com/BpUGK3y.png) ![](https://i.imgur.com/XNh2CRJ.png) ``` #include <bits/stdc++.h> using namespace std; typedef long long ll; char nc() { static char buf[1000000], *p = buf, *q = buf; return p == q && (q = (p = buf) + fread(buf, 1, 1000000, stdin), p == q) ? EOF : *p++; } inline ll rd() { // LLONG_MIN LMAX=9,223,372,036,854,775,807 ll s = 0, w = 1; char ch = nc(); while (ch < '0' || ch > '9') { if (ch == '-') w = -1; ch = nc(); } while (ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = nc(); return s * w; } const int N = 1e5 + 7; struct Tree { int mx, _mx; int ad, _ad; int st, _st; } g[N * 4]; int a[N]; int n, m; #define ls u * 2 #define rs u * 2 + 1 #define mid (l + r) / 2 inline void pushup(int u) { g[u].mx = max(g[ls].mx, g[rs].mx); g[u]._mx = max(g[ls]._mx, g[rs]._mx); } inline void pushadd(int u, int v, int _v) { g[u]._mx = max(g[u]._mx, g[u].mx + _v), g[u].mx += v; if (g[u].st == INT_MIN) g[u]._ad = max(g[u]._ad, g[u].ad + _v), g[u].ad += v; else g[u]._st = max(g[u]._st, g[u].st + _v), g[u].st += v; } inline void pushset(int u, int v, int _v) { g[u]._mx = max(g[u]._mx, _v), g[u].mx = v; g[u]._st = max(g[u]._st, _v), g[u].st = v; } inline void pushdown(int u, int l, int r) { if (g[u].ad) pushadd(ls, g[u].ad, g[u]._ad), pushadd(rs, g[u].ad, g[u]._ad), g[u].ad = g[u]._ad = 0; if (g[u].st != INT_MIN) pushset(ls, g[u].st, g[u]._st), pushset(rs, g[u].st, g[u]._st), g[u].st = g[u]._st = INT_MIN; } void build(int u = 1, int l = 1, int r = n) { g[u]._st = g[u].st = INT_MIN; if (l == r) { g[u].mx = a[l]; g[u]._mx = a[l]; return; } build(ls, l, mid), build(rs, mid + 1, r); pushup(u); } int L, R; void add(int v, int u = 1, int l = 1, int r = n) { if (R < l || r < L) return; if (L <= l && r <= R) return pushadd(u, v, max(v, 0)); pushdown(u, l, r); add(v, ls, l, mid), add(v, rs, mid + 1, r); pushup(u); } void tset(int v, int u = 1, int l = 1, int r = n) { if (R < l || r < L) return; if (L <= l && r <= R) return pushset(u, v, v); pushdown(u, l, r); tset(v, ls, l, mid), tset(v, rs, mid + 1, r); pushup(u); } int qmax(int u = 1, int l = 1, int r = n) { if (R < l || r < L) return INT_MIN; if (L <= l && r <= R) return g[u].mx; pushdown(u, l, r); return max(qmax(ls, l, mid), qmax(rs, mid + 1, r)); } int qmaxh(int u = 1, int l = 1, int r = n) { if (R < l || r < L) return INT_MIN; if (L <= l && r <= R) return g[u]._mx; pushdown(u, l, r); return max(qmaxh(ls, l, mid), qmaxh(rs, mid + 1, r)); } int main() { n = rd(); for (int i = 1; i <= n; ++i) a[i] = rd(); build(); int m = rd(), z; for (int i = 1; i <= m; ++i) { char op = nc(); while (op == ' ' || op == '\r' || op == '\n') op = nc(); L = rd(), R = rd(); if (op == 'Q') printf("%d\n", qmax()); else if (op == 'A') printf("%d\n", qmaxh()); else if (op == 'P') add(rd()); else tset(rd()); } return 0; } ``` ### 主席树 ``` const int maxn = 1e5; // 数据范围 int tot, n, m; int sum[(maxn << 5) + 10], rt[maxn + 10], ls[(maxn << 5) + 10], rs[(maxn << 5) + 10]; int a[maxn + 10], ind[maxn + 10], len; inline int getid(const int &val) { // 离散化 return lower_bound(ind + 1, ind + len + 1, val) - ind; } int build(int l, int r) { // 建树 int root = ++tot; if (l == r) return root; int mid = l + r >> 1; ls[root] = build(l, mid); rs[root] = build(mid + 1, r); return root; // 返回该子树的根节点 } int update(int k, int l, int r, int root) { // 插入操作 int dir = ++tot; ls[dir] = ls[root], rs[dir] = rs[root], sum[dir] = sum[root] + 1; if (l == r) return dir; int mid = l + r >> 1; if (k <= mid) ls[dir] = update(k, l, mid, ls[dir]); else rs[dir] = update(k, mid + 1, r, rs[dir]); return dir; } int query(int u, int v, int l, int r, int k) { // 查询操作 int mid = l + r >> 1, x = sum[ls[v]] - sum[ls[u]]; // 通过区间减法得到左儿子的信息 if (l == r) return l; if (k <= x) // 说明在左儿子中 return query(ls[u], ls[v], l, mid, k); else // 说明在右儿子中 return query(rs[u], rs[v], mid + 1, r, k - x); } inline void init() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; ++i) scanf("%d", a + i); memcpy(ind, a, sizeof ind); sort(ind + 1, ind + n + 1); len = unique(ind + 1, ind + n + 1) - ind - 1; rt[0] = build(1, len); for (int i = 1; i <= n; ++i) rt[i] = update(getid(a[i]), 1, len, rt[i - 1]); } ``` ### 树链剖分 ``` const int maxn=1e5+5; struct tree_{ int tot=0,head[maxn],Next[maxn*2],to[maxn*2]; int siz[maxn],top[maxn],son[maxn],dep[maxn],fa[maxn],dfn[maxn],rnk[maxn],cnt; void init(int n){ tot=0; for (int i=1; i<=n; i++){ head[i]=-1; } } void add_edge(int a,int b){ Next[tot]=head[a],to[tot]=b; head[a]=tot++; } void dfs1(int x) { son[x]=-1; siz[x]=1; for (int i=head[x]; i=-1; i=Next[i]){ int y=to[i]; if (!dep[y]) { dep[y]=dep[x]+1; fa[y]=x; dfs1(y); siz[x]+=siz[y]; if (son[x]==-1 || siz[y]>siz[son[x]]) son[x]=y; } } } void dfs2(int x,int t) { top[x]=t; cnt++; dfn[x]=cnt; rnk[cnt]=x; if (son[x]==-1) return; dfs2(son[x],t); for (int i=head[x]; i!=-1; i=Next[i]){ int y=to[i]; if (y!=son[x] && y!=fa[x]) dfs2(y,y); } } }tr; ``` ## DP优化 ### 斜率优化 ![](https://i.imgur.com/jITOSQY.png) ![](https://i.imgur.com/wwtrMYi.png) ![](https://i.imgur.com/8rjHpIt.png) ### 四边形不等式优化 ![](https://i.imgur.com/T9T8B17.png) ![](https://i.imgur.com/7fMlNn1.png) ![](https://i.imgur.com/B48sbJu.png) ![](https://i.imgur.com/mcUpCcg.png) ## 数学 ### 高精度板子 ``` #include<bits/stdc++.h> using namespace std; const int maxn = 1000; struct bign{ int d[maxn], len; void clean() { while(len > 1 && !d[len-1]) len--; } bign() { memset(d, 0, sizeof(d)); len = 1; } bign(int num) { *this = num; } bign(char* num) { *this = num; } bign operator = (const char* num){ memset(d, 0, sizeof(d)); len = strlen(num); for(int i = 0; i < len; i++) d[i] = num[len-1-i] - '0'; clean(); return *this; } bign operator = (int num){ char s[20]; sprintf(s, "%d", num); *this = s; return *this; } bign operator + (const bign& b){ bign c = *this; int i; for (i = 0; i < b.len; i++){ c.d[i] += b.d[i]; if (c.d[i] > 9) c.d[i]%=10, c.d[i+1]++; } while (c.d[i] > 9) c.d[i++]%=10, c.d[i]++; c.len = max(len, b.len); if (c.d[i] && c.len <= i) c.len = i+1; return c; } bign operator - (const bign& b){ bign c = *this; int i; for (i = 0; i < b.len; i++){ c.d[i] -= b.d[i]; if (c.d[i] < 0) c.d[i]+=10, c.d[i+1]--; } while (c.d[i] < 0) c.d[i++]+=10, c.d[i]--; c.clean(); return c; } bign operator * (const bign& b)const{ int i, j; bign c; c.len = len + b.len; for(j = 0; j < b.len; j++) for(i = 0; i < len; i++) c.d[i+j] += d[i] * b.d[j]; for(i = 0; i < c.len-1; i++) c.d[i+1] += c.d[i]/10, c.d[i] %= 10; c.clean(); return c; } bign operator / (const bign& b){ int i, j; bign c = *this, a = 0; for (i = len - 1; i >= 0; i--) { a = a*10 + d[i]; for (j = 0; j < 10; j++) if (a < b*(j+1)) break; c.d[i] = j; a = a - b*j; } c.clean(); return c; } bign operator % (const bign& b){ int i, j; bign a = 0; for (i = len - 1; i >= 0; i--) { a = a*10 + d[i]; for (j = 0; j < 10; j++) if (a < b*(j+1)) break; a = a - b*j; } return a; } bign operator += (const bign& b){ *this = *this + b; return *this; } bool operator <(const bign& b) const{ if(len != b.len) return len < b.len; for(int i = len-1; i >= 0; i--) if(d[i] != b.d[i]) return d[i] < b.d[i]; return false; } bool operator >(const bign& b) const{return b < *this;} bool operator<=(const bign& b) const{return !(b < *this);} bool operator>=(const bign& b) const{return !(*this < b);} bool operator!=(const bign& b) const{return b < *this || *this < b;} bool operator==(const bign& b) const{return !(b < *this) && !(b > *this);} string str() const{ char s[maxn]={}; for(int i = 0; i < len; i++) s[len-1-i] = d[i]+'0'; return s; } }; istream& operator >> (istream& in, bign& x) { string s; in >> s; x = s.c_str(); return in; } ostream& operator << (ostream& out, const bign& x) { out << x.str(); return out; } ``` ### 卡特兰数 ``` 给定n个0和n个1,它们按照某种顺序排成长度为2n的序列,满足任意前缀中0的个数都不少于1的个数的序列的数量为: Cat(n) = C(2n, n) / (n + 1) ``` ### 快速幂 ``` ll qpow(ll a, ll b) { ll res = 1; while (b) { if (b & 1) res = res * a % mod; a = a * a % mod; b >>= 1; } return res; } ``` ### LCM ``` //lcm ll lcm(int a, int b) { return a / __gcd(a, b) * b; } ``` ### 拓展GCD ``` //扩展gcd ll extend_gcd(ll a, ll b, ll &x, ll &y) { if (a == 0 && b == 0) return -1ll; if (b == 0) { x = 1ll; y = 0ll; return a; } ll d = extend_gcd(b, a % b, y, x); y -= a / b * x; return d; } ``` ### 求逆元 ``` //逆元 nlogn ll mod_reverse(ll a, ll n) { ll x, y, d = extend_gcd(a, n, x, y); if (d == 1) { if (x % n <= 0) return x % n + n; else return x % n; } else return -1ll; } ``` ``` //逆元 n int inv[N]; void pre_inv() { inv[1] = 1; for (int i = 2; i < mod; i++) { int a = mod / i, b = mod % i; inv[i] = (1LL * inv[b] * (-a) % mod + mod) % mod; } } ``` ### 线性素数筛 ``` bool is_prime[1000100] = {1, 1}; ll prime[100010], cnt = 0; // 素数线性筛 void euler(int p = 1000100) { for (long long i = 2; i <= p; ++i) { if (!is_prime[i]) prime[++cnt] = i; for (long long j = 1; j <= cnt && i * prime[j] <= p; ++j) { is_prime[i * prime[j]] = true; if (i % prime[j] == 0) break; } } } ``` ### 自适应辛普森 ``` /* 自适应辛普森,用于连续函数积分 */ double f(double x)//define the function for yourself! {} double simpson(double l, double r) { double mid = (l + r) / 2; return (r - l) * (f(l) + 4 * f(mid) + f(r)) / 6; } double asr(double l, double r, double eqs, double ans, int step) { double mid = (l + r) / 2; double fl = simpson(l, mid), fr = simpson(mid, r); if (abs(fl + fr - ans) <= 15 * eqs && step < 0) return fl + fr + (fl + fr - ans) / 15; return asr(l, mid, eqs / 2, fl, step - 1) + asr(mid, r, eqs / 2, fr, step - 1); } double calc(double l, double r, double eps = 1e-8) { return asr(l, r, eps, simpson(l, r), 12); } ``` ### FFT ``` class FFT { public: typedef pair<ll, ll> P; typedef complex<double> cp; const static ll inf = 0x3f3f3f3f; const static int mod = 1e9 + 7; const static int maxn = 2e6 + 5; const static double pi = 3.14159265; int n, m, v; int rev[maxn + maxn & (-maxn) + 10], res[maxn + maxn & (-maxn) + 10]; cp p[maxn + maxn & (-maxn) + 10], q[maxn + maxn & (-maxn) + 10]; FFT(int n, int m) : n(n), m(m) { v = 2; while (v < n + m) v *= 2; memset(rev, 0, sizeof(rev)); memset(res, 0, sizeof(res)); memset(p, 0, sizeof(p)); memset(q, 0, sizeof(q)); } void input() { for (int i = 0; i <= n; i++) cin >> p[i]; for (int i = 0; i <= m; i++) cin >> q[i]; } void display() { for (int i = 0; i <= n + m; i++) { cout << res[i] << ' '; } cout << endl; } void fft(cp *a, int n, int inv) { int bit = 0; while ((1 << bit) < n) bit++; for (int i = 0; i < n; i++) { rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (bit - 1)); if (i < rev[i]) swap(a[i], a[rev[i]]); //不加这条if会交换两次(就是没交换) } for (int mid = 1; mid < n; mid *= 2) //mid是准备合并序列的长度的二分之一 { cp temp(cos(pi / mid), inv * sin(pi / mid)); //单位根,pi的系数2已经约掉了 for (int i = 0; i < n; i += mid * 2) //mid*2是准备合并序列的长度,i是合并到了哪一位 { cp omega(1, 0); for (int j = 0; j < mid; j++, omega *= temp) //只扫左半部分,得到右半部分的答案 { cp x = a[i + j], y = omega * a[i + j + mid]; a[i + j] = x + y, a[i + j + mid] = x - y; //这个就是蝴蝶变换什么的 } } } } void multi() { fft(p, v, 1); fft(q, v, 1); for (int i = 0; i < v; i++) p[i] *= q[i]; fft(p, v, -1); for (int i = 0; i < v; i++) res[i] = (int)(p[i].real() / v + 0.5); } void BigMulti(char *a, char *b, char *&res) { res = new char[v + 100]; int ptr = 0; for (int i = 0; i <= n; i++) { p[i] = cp(a[n - i] - '0', 0); } for (int i = 0; i <= m; i++) { q[i] = cp(b[m - i] - '0', 0); } multi(); int temp = 0; for (int i = 0; i <= n + m; i++) { int now = temp + res[i]; res[ptr] = char('0' + now % 10); ptr++; temp = now / 10; } while (temp) { res[ptr] = char(temp % 10 + '0'); ptr++; temp /= 10; } } }; ll excrt(vector<P> p) { ll m = 0, b = 0; for (auto it : p) { if (b == 0) { m = it.second; b = it.first; } else { ll x, y; ll a1 = b, a2 = it.first; ll m1 = m, m2 = it.second; ll gcd = extend_gcd(m1, m2, x, y); x = qpow(x, ((a2 - b % m2 + m2) % m2) / gcd, m2 / gcd); b += m * x, m *= m2 / gcd; b = (b % m + m) % m; } } return b; } ``` ### 杜教筛 ``` class dujiaoshai { const static ll N = 1e6 + 10, INF = 0x3f3f3f3f, mod = 1e9 + 7; ll n, m, p; ll phi[N], s[N], prime[N], vis[N], tot, d[N]; ll qmi(ll a, ll b, ll mod) { ll res = 1; while (b) { if (b & 1) res = res * a % mod; b >>= 1; a = a * a % mod; } return res; } map<ll, ll> mp; ll Mod(ll n, ll mod) { return (n % mod + mod) % mod; } ll calc_S(ll n, ll mod) { if (n < N) return s[n]; if (mp.count(n)) return mp[n]; ll ans = Mod(1ll * n * (n + 1) / 2, mod); for (ll l = 2, r; l <= n; l = r + 1) { r = n / (n / l); ans = Mod((ans - 1ll * (r - l + 1) % mod * calc_S(n / l, mod) % mod) % mod, mod); } return mp[n] = ans; } ll calc_d(ll n, ll mod) { if (n < N) return d[n]; ll ans = 0; for (ll l = 1, r; l <= n; l = r + 1) { r = n / (n / l); ll sum = (1ll * (r - l + 1) * (r + l) / 2) % mod; ll pre = (1ll * (1 + n / l) * (n / l) / 2) % mod; ans = Mod(ans + 1ll * sum * pre % mod, mod); } return ans; } void init(ll mod) { phi[1] = 1; for (ll i = 2; i < N; i++) { if (!vis[i]) prime[++tot] = i, phi[i] = i - 1; for (ll j = 1; j <= tot && i * prime[j] < N; j++) { vis[i * prime[j]] = 1; if (i % prime[j] == 0) { phi[i * prime[j]] = phi[i] * prime[j]; break; } phi[i * prime[j]] = phi[i] * phi[prime[j]]; } } for (ll i = 1; i < N; i++) for (ll j = i; j < N; j += i) d[j]++; for (ll i = 1; i < N; i++) { d[i] = (d[i - 1] + 1ll * i * d[i] % mod) % mod; s[i] = (s[i - 1] + phi[i] % mod) % mod; } } }; ``` ### 欧拉函数表 ``` //欧拉函数表 int phi[1000000]; void phi_table(int n) { for (int i = 2; i <= n; i++) phi[i] = 0; phi[1] = 1; for (int i = 2; i <= n; i++) { if (!phi[i]) { for (int j = i; j <= n; j += i) { if (!phi[j]) phi[j] = j; phi[j] = phi[j] / i * (i - 1); } } } } ``` ### 米勒罗宾质数判定 用于1e12+大数质数判定。 ``` //米勒罗宾素数判定 bool millerRabbin(int n) { int a = n - 1, b = 0; int s = 100; while (a % 2 == 0) a /= 2, ++b; for (int i = 1, j; i <= s; ++i) { int x = rand() % (n - 2) + 2, v = qpow(x, a, n); if (v == 1 || v == n - 1) continue; for (j = 0; j < b; ++j) { v = (long long)v * v % n; if (v == n - 1) break; } if (j >= b) return 0; } return 1; } ``` ### 单个欧拉值 ``` //单个欧拉函数值 int euler_phi(int n) { int m = int(sqrt(n + 0.5)); int ans = n; for (int i = 2; i <= m; i++) if (n % i == 0) { ans = ans / i * (i - 1); while (n % i == 0) n /= i; } if (n > 1) ans = ans / n * (n - 1); return ans; } ``` ### 斐波那契数列相关操作 ``` struct fib { ll ver[2][2]; fib() { ver[0][0] = ver[0][1] = ver[1][0] = 1; } //快速幂,算F(n+m) fib operator*(fib y) const { fib ans; ll res = 0; for (ll i = 0; i < 2; i++) { for (ll j = 0; j < 2; j++) { res = 0; for (ll k = 0; k < 2; k++) { res = (res + ver[i][k] * y.ver[k][j] % mod) % mod; } ans.ver[i][j] = res; } } return ans; } //求第n个斐波那契数 ll Fib(ll n) { fib res, a; while (n) { if (n & 1) res = res * a; a = a * a; n >>= 1; } return res.ver[1][1]; } //大数取模 string p; ll Mod(ll mmod) { ll res = 0; for (ll i = 0; i < p.length(); i++) { res = (res * 10 + p[i] - '0') % mmod; } return res; } //找斐波那契数列循环结 ll lcm(ll a, ll b) { return b / __gcd(a, b) * a; } ll getLooper(ll v) { if (v == 5) return 20; if (v % 5 == 2 || v % 5 == 3) return v * 2 + 2; return v - 1; } ll getLoop(ll mmod) { ll ans = 1; for (ll i = 1; i <= cnt; i++) { if (mmod % prime[i] == 0) { ll v = 1; while (mmod % prime[i] == 0) v *= prime[i], mmod /= prime[i]; ans = lcm(ans, v / prime[i] * getLooper(prime[i])); } } if (mmod != 1) ans = lcm(ans, getLooper(mmod)); return ans; } }; ``` ### (拓展)中国剩余定理相关 ``` ll excrt(vector<P> p) { ll m = 0, b = 0; for (auto it : p) { if (b == 0) { m = it.second; b = it.first; } else { ll x, y; ll a1 = b, a2 = it.first; ll m1 = m, m2 = it.second; ll gcd = extend_gcd(m1, m2, x, y); x = qpow(x, ((a2 - b % m2 + m2) % m2) / gcd, m2 / gcd); b += m * x, m *= m2 / gcd; b = (b % m + m) % m; } } return b; } ``` ### 多项式 ``` 空间非常优秀但是时间偏慢。 namespace fstdlib { typedef long long ll; int mod = 998244353, grt = 3; class poly { private: std::vector<int> data; public: poly(std::size_t len = std::size_t(0)) { data = std::vector<int>(len); } poly(const std::vector<int> &b) { data = b; } poly(const poly &b) { data = b.data; } void resize(std::size_t len, int val = 0) { data.resize(len, val); } std::size_t size(void) const { return data.size(); } void clear(void) { data.clear(); } #if __cplusplus >= 201103L void shrink_to_fit(void) { data.shrink_to_fit(); } #endif void out(void) { for (int i = 0; i < (int)data.size(); ++i) printf("%d ", data[i]); puts(""); } int &operator[](std::size_t b) { return data[b]; } const int &operator[](std::size_t b) const { return data[b]; } poly operator*(const poly &h) const; poly operator*=(const poly &h); poly operator*(const int &h) const; poly operator*=(const int &h); poly operator+(const poly &h) const; poly operator+=(const poly &h); poly operator-(const poly &h) const; poly operator-=(const poly &h); poly operator<<(const std::size_t &b) const; poly operator<<=(const std::size_t &b); poly operator>>(const std::size_t &b) const; poly operator>>=(const std::size_t &b); poly operator/(const int &h) const; poly operator/=(const int &h); poly operator==(const poly &h) const; poly operator!=(const poly &h) const; poly operator+(const int &h) const; poly operator+=(const int &h); poly inv(void) const; poly inv(const int &h) const; friend poly sqrt(const poly &h); friend poly log(const poly &h); friend poly exp(const poly &h); }; int qpow(int a, int b, int p = mod) { int res = 1; while (b) { if (b & 1) res = (ll)res * a % p; a = (ll)a * a % p, b >>= 1; } return res; } std::vector<int> rev; void dft_for_module(std::vector<int> &f, int n, int b) { static std::vector<int> w; w.resize(n); for (int i = 0; i < n; ++i) if (i < rev[i]) std::swap(f[i], f[rev[i]]); for (int i = 2; i <= n; i <<= 1) { w[0] = 1, w[1] = qpow(grt, (mod - 1) / i); if (b == -1) w[1] = qpow(w[1], mod - 2); for (int j = 2; j < i / 2; ++j) w[j] = (ll)w[j - 1] * w[1] % mod; for (int j = 0; j < n; j += i) for (int k = 0; k < i / 2; ++k) { int p = f[j + k], q = (ll)f[j + k + i / 2] * w[k] % mod; f[j + k] = (p + q) % mod, f[j + k + i / 2] = (p - q + mod) % mod; } } } poly poly::operator*(const poly &h) const { int N = 1; while (N < (int)(size() + h.size() - 1)) N <<= 1; std::vector<int> f(this->data), g(h.data); f.resize(N), g.resize(N); rev.resize(N); for (int i = 0; i < N; ++i) rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? N >> 1 : 0); dft_for_module(f, N, 1), dft_for_module(g, N, 1); for (int i = 0; i < N; ++i) f[i] = (ll)f[i] * g[i] % mod; dft_for_module(f, N, -1), f.resize(size() + h.size() - 1); for (int i = 0, inv = qpow(N, mod - 2); i < (int)f.size(); ++i) f[i] = (ll)f[i] * inv % mod; return f; } poly poly::operator*=(const poly &h) { return *this = *this * h; } poly poly::operator*(const int &h) const { std::vector<int> f(this->data); for (int i = 0; i < (int)f.size(); ++i) f[i] = (ll)f[i] * h % mod; return f; } poly poly::operator*=(const int &h) { for (int i = 0; i < (int)size(); ++i) data[i] = (ll)data[i] * h % mod; return *this; } poly poly::operator+(const poly &h) const { std::vector<int> f(this->data); if (f.size() < h.size()) f.resize(h.size()); for (int i = 0; i < (int)h.size(); ++i) f[i] = (f[i] + h[i]) % mod; return f; } poly poly::operator+=(const poly &h) { std::vector<int> &f = this->data; if (f.size() < h.size()) f.resize(h.size()); for (int i = 0; i < (int)h.size(); ++i) f[i] = (f[i] + h[i]) % mod; return f; } poly poly::operator-(const poly &h) const { std::vector<int> f(this->data); if (f.size() < h.size()) f.resize(h.size()); for (int i = 0; i < (int)h.size(); ++i) f[i] = (f[i] - h[i] + mod) % mod; return f; } poly poly::operator-=(const poly &h) { std::vector<int> &f = this->data; if (f.size() < h.size()) f.resize(h.size()); for (int i = 0; i < (int)h.size(); ++i) f[i] = (f[i] - h[i] + mod) % mod; return f; } poly poly::operator<<(const std::size_t &b) const { std::vector<int> f(size() + b); for (int i = 0; i < (int)size(); ++i) f[i + b] = data[i]; return f; } poly poly::operator<<=(const std::size_t &b) { return *this = (*this) << b; } poly poly::operator>>(const std::size_t &b) const { std::vector<int> f(size() - b); for (int i = 0; i < (int)f.size(); ++i) f[i] = data[i + b]; return f; } poly poly::operator>>=(const std::size_t &b) { return *this = (*this) >> b; } poly poly::operator/(const int &h) const { std::vector<int> f(this->data); int inv = qpow(h, mod - 2); for (int i = 0; i < (int)f.size(); ++i) f[i] = (ll)f[i] * inv % mod; return f; } poly poly::operator/=(const int &h) { int inv = qpow(h, mod - 2); for (int i = 0; i < (int)data.size(); ++i) data[i] = (ll)data[i] * inv % mod; return *this; } poly poly::inv(void) const { int N = 1; while (N < (int)(size() + size() - 1)) N <<= 1; std::vector<int> f(N), g(N), d(this->data); d.resize(N), f[0] = qpow(d[0], mod - 2); for (int w = 2; w < N; w <<= 1) { for (int i = 0; i < w; ++i) g[i] = d[i]; rev.resize(w << 1); for (int i = 0; i < w * 2; ++i) rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? w : 0); dft_for_module(f, w << 1, 1), dft_for_module(g, w << 1, 1); for (int i = 0; i < w * 2; ++i) f[i] = (ll)f[i] * (2 + mod - (ll)f[i] * g[i] % mod) % mod; dft_for_module(f, w << 1, -1); for (int i = 0, inv = qpow(w << 1, mod - 2); i < w; ++i) f[i] = (ll)f[i] * inv % mod; for (int i = w; i < w * 2; ++i) f[i] = 0; } f.resize(size()); return f; } poly poly::operator==(const poly &h) const { if (size() != h.size()) return 0; for (int i = 0; i < (int)size(); ++i) if (data[i] != h[i]) return 0; return 1; } poly poly::operator!=(const poly &h) const { if (size() != h.size()) return 1; for (int i = 0; i < (int)size(); ++i) if (data[i] != h[i]) return 1; return 0; } poly poly::operator+(const int &h) const { poly f(this->data); f[0] = (f[0] + h) % mod; return f; } poly poly::operator+=(const int &h) { return *this = (*this) + h; } poly poly::inv(const int &h) const { poly f(*this); f.resize(h); return f.inv(); } int modsqrt(int h, int p = mod) { return 1; } poly sqrt(const poly &h) { int N = 1; while (N < (int)(h.size() + h.size() - 1)) N <<= 1; poly f(N), g(N), d(h); d.resize(N), f[0] = modsqrt(d[0]); for (int w = 2; w < N; w <<= 1) { g.resize(w); for (int i = 0; i < w; ++i) g[i] = d[i]; f = (f + f.inv(w) * g) / 2; f.resize(w); } f.resize(h.size()); return f; } poly log(const poly &h) { poly f(h); for (int i = 1; i < (int)f.size(); ++i) f[i - 1] = (ll)f[i] * i % mod; f[f.size() - 1] = 0, f = f * h.inv(), f.resize(h.size()); for (int i = (int)f.size() - 1; i > 0; --i) f[i] = (ll)f[i - 1] * qpow(i, mod - 2) % mod; f[0] = 0; return f; } poly exp(const poly &h) { int N = 1; while (N < (int)(h.size() + h.size() - 1)) N <<= 1; poly f(N), g(N), d(h); f[0] = 1, d.resize(N); for (int w = 2; w < N; w <<= 1) { f.resize(w), g.resize(w); for (int i = 0; i < w; ++i) g[i] = d[i]; f = f * (g + 1 - log(f)); f.resize(w); } f.resize(h.size()); return f; } struct comp { long double x, y; comp(long double _x = 0, long double _y = 0) : x(_x), y(_y) {} comp operator*(const comp &b) const { return comp(x * b.x - y * b.y, x * b.y + y * b.x); } comp operator+(const comp &b) const { return comp(x + b.x, y + b.y); } comp operator-(const comp &b) const { return comp(x - b.x, y - b.y); } comp conj(void) { return comp(x, -y); } }; const int EPS = 1e-9; template <typename FLOAT_T> FLOAT_T fabs(const FLOAT_T &x) { return x > 0 ? x : -x; } template <typename FLOAT_T> FLOAT_T sin(const FLOAT_T &x, const long double &EPS = fstdlib::EPS) { FLOAT_T res = 0, delt = x; int d = 0; while (fabs(delt) > EPS) { res += delt, ++d; delt *= -x * x / ((2 * d) * (2 * d + 1)); } return res; } template <typename FLOAT_T> FLOAT_T cos(const FLOAT_T &x, const long double &EPS = fstdlib::EPS) { FLOAT_T res = 0, delt = 1; int d = 0; while (fabs(delt) > EPS) { res += delt, ++d; delt *= -x * x / ((2 * d) * (2 * d - 1)); } return res; } const long double PI = std::acos((long double)(-1)); void dft_for_complex(std::vector<comp> &f, int n, int b) { static std::vector<comp> w; w.resize(n); for (int i = 0; i < n; ++i) if (i < rev[i]) std::swap(f[i], f[rev[i]]); for (int i = 2; i <= n; i <<= 1) { w[0] = comp(1, 0), w[1] = comp(cos(2 * PI / i), b * sin(2 * PI / i)); for (int j = 2; j < i / 2; ++j) w[j] = w[j - 1] * w[1]; for (int j = 0; j < n; j += i) for (int k = 0; k < i / 2; ++k) { comp p = f[j + k], q = f[j + k + i / 2] * w[k]; f[j + k] = p + q, f[j + k + i / 2] = p - q; } } } class arbitrary_module_poly { private: std::vector<int> data; int construct_element(int D, ll x, ll y, ll z) const { x %= mod, y %= mod, z %= mod; return ((ll)D * D * x % mod + (ll)D * y % mod + z) % mod; } public: int mod; arbitrary_module_poly(std::size_t len = std::size_t(0), int module_value = 1e9 + 7) { mod = module_value; data = std::vector<int>(len); } arbitrary_module_poly(const std::vector<int> &b, int module_value = 1e9 + 7) { mod = module_value; data = b; } arbitrary_module_poly(const arbitrary_module_poly &b) { mod = b.mod; data = b.data; } void resize(std::size_t len, const int &val = 0) { data.resize(len, val); } std::size_t size(void) const { return data.size(); } void clear(void) { data.clear(); } #if __cplusplus >= 201103L void shrink_to_fit(void) { data.shrink_to_fit(); } #endif int &operator[](std::size_t b) { return data[b]; } const int &operator[](std::size_t b) const { return data[b]; } arbitrary_module_poly operator*(const arbitrary_module_poly &h) const; arbitrary_module_poly operator*=(const arbitrary_module_poly &h); arbitrary_module_poly operator*(const int &h) const; arbitrary_module_poly operator*=(const int &h); arbitrary_module_poly operator+(const arbitrary_module_poly &h) const; arbitrary_module_poly operator+=(const arbitrary_module_poly &h); arbitrary_module_poly operator-(const arbitrary_module_poly &h) const; arbitrary_module_poly operator-=(const arbitrary_module_poly &h); arbitrary_module_poly operator<<(const std::size_t &b) const; arbitrary_module_poly operator<<=(const std::size_t &b); arbitrary_module_poly operator>>(const std::size_t &b) const; arbitrary_module_poly operator>>=(const std::size_t &b); arbitrary_module_poly operator/(const int &h) const; arbitrary_module_poly operator/=(const int &h); arbitrary_module_poly operator==(const arbitrary_module_poly &h) const; arbitrary_module_poly operator!=(const arbitrary_module_poly &h) const; arbitrary_module_poly inv(void) const; arbitrary_module_poly inv(const int &h) const; friend arbitrary_module_poly sqrt(const arbitrary_module_poly &h); friend arbitrary_module_poly log(const arbitrary_module_poly &h); }; arbitrary_module_poly arbitrary_module_poly::operator*( const arbitrary_module_poly &h) const { int N = 1; while (N < (int)(size() + h.size() - 1)) N <<= 1; std::vector<comp> f(N), g(N), p(N), q(N); const int D = std::sqrt(mod); for (int i = 0; i < (int)size(); ++i) f[i].x = data[i] / D, f[i].y = data[i] % D; for (int i = 0; i < (int)h.size(); ++i) g[i].x = h[i] / D, g[i].y = h[i] % D; rev.resize(N); for (int i = 0; i < N; ++i) rev[i] = (rev[i >> 1] >> 1) | (i & 1 ? N >> 1 : 0); dft_for_complex(f, N, 1), dft_for_complex(g, N, 1); for (int i = 0; i < N; ++i) { p[i] = (f[i] + f[(N - i) % N].conj()) * comp(0.50, 0) * g[i]; q[i] = (f[i] - f[(N - i) % N].conj()) * comp(0, -0.5) * g[i]; } dft_for_complex(p, N, -1), dft_for_complex(q, N, -1); std::vector<int> r(size() + h.size() - 1); for (int i = 0; i < (int)r.size(); ++i) r[i] = construct_element(D, p[i].x / N + 0.5, (p[i].y + q[i].x) / N + 0.5, q[i].y / N + 0.5); return arbitrary_module_poly(r, mod); } arbitrary_module_poly arbitrary_module_poly::operator*=( const arbitrary_module_poly &h) { return *this = *this * h; } arbitrary_module_poly arbitrary_module_poly::operator*(const int &h) const { std::vector<int> f(this->data); for (int i = 0; i < (int)f.size(); ++i) f[i] = (ll)f[i] * h % mod; return arbitrary_module_poly(f, mod); } arbitrary_module_poly arbitrary_module_poly::operator*=(const int &h) { for (int i = 0; i < (int)size(); ++i) data[i] = (ll)data[i] * h % mod; return *this; } arbitrary_module_poly arbitrary_module_poly::operator+( const arbitrary_module_poly &h) const { std::vector<int> f(this->data); if (f.size() < h.size()) f.resize(h.size()); for (int i = 0; i < (int)h.size(); ++i) f[i] = (f[i] + h[i]) % mod; return arbitrary_module_poly(f, mod); } arbitrary_module_poly arbitrary_module_poly::operator+=( const arbitrary_module_poly &h) { if (size() < h.size()) resize(h.size()); for (int i = 0; i < (int)h.size(); ++i) data[i] = (data[i] + h[i]) % mod; return *this; } arbitrary_module_poly arbitrary_module_poly::operator-( const arbitrary_module_poly &h) const { std::vector<int> f(this->data); if (f.size() < h.size()) f.resize(h.size()); for (int i = 0; i < (int)h.size(); ++i) f[i] = (f[i] + mod - h[i]) % mod; return arbitrary_module_poly(f, mod); } arbitrary_module_poly arbitrary_module_poly::operator-=( const arbitrary_module_poly &h) { if (size() < h.size()) resize(h.size()); for (int i = 0; i < (int)h.size(); ++i) data[i] = (data[i] + mod - h[i]) % mod; return *this; } arbitrary_module_poly arbitrary_module_poly::operator<<( const std::size_t &b) const { std::vector<int> f(size() + b); for (int i = 0; i < (int)size(); ++i) f[i + b] = data[i]; return arbitrary_module_poly(f, mod); } arbitrary_module_poly arbitrary_module_poly::operator<<=(const std::size_t &b) { return *this = (*this) << b; } arbitrary_module_poly arbitrary_module_poly::operator>>( const std::size_t &b) const { std::vector<int> f(size() - b); for (int i = 0; i < (int)f.size(); ++i) f[i] = data[i + b]; return arbitrary_module_poly(f, mod); } arbitrary_module_poly arbitrary_module_poly::operator>>=(const std::size_t &b) { return *this = (*this) >> b; } arbitrary_module_poly arbitrary_module_poly::inv(void) const { int N = 1; while (N < (int)(size() + size() - 1)) N <<= 1; arbitrary_module_poly f(1, mod), g(N, mod), h(*this), f2(1, mod); f[0] = qpow(data[0], mod - 2, mod), h.resize(N), f2[0] = 2; for (int w = 2; w < N; w <<= 1) { g.resize(w); for (int i = 0; i < w; ++i) g[i] = h[i]; f = f * (f * g - f2) * (mod - 1); f.resize(w); } f.resize(size()); return f; } arbitrary_module_poly arbitrary_module_poly::inv(const int &h) const { arbitrary_module_poly f(*this); f.resize(h); return f.inv(); } arbitrary_module_poly arbitrary_module_poly::operator/(const int &h) const { int inv = qpow(h, mod - 2, mod); std::vector<int> f(this->data); for (int i = 0; i < (int)f.size(); ++i) f[i] = (ll)f[i] * inv % mod; return arbitrary_module_poly(f, mod); } arbitrary_module_poly arbitrary_module_poly::operator/=(const int &h) { int inv = qpow(h, mod - 2, mod); for (int i = 0; i < (int)size(); ++i) data[i] = (ll)data[i] * inv % mod; return *this; } arbitrary_module_poly arbitrary_module_poly::operator==( const arbitrary_module_poly &h) const { if (size() != h.size() || mod != h.mod) return 0; for (int i = 0; i < (int)size(); ++i) if (data[i] != h[i]) return 0; return 1; } arbitrary_module_poly arbitrary_module_poly::operator!=( const arbitrary_module_poly &h) const { if (size() != h.size() || mod != h.mod) return 1; for (int i = 0; i < (int)size(); ++i) if (data[i] != h[i]) return 1; return 0; } arbitrary_module_poly sqrt(const arbitrary_module_poly &h) { int N = 1; while (N < (int)(h.size() + h.size() - 1)) N <<= 1; arbitrary_module_poly f(1, mod), g(N, mod), d(h); f[0] = modsqrt(h[0], mod), d.resize(N); for (int w = 2; w < N; w <<= 1) { g.resize(w); for (int i = 0; i < w; ++i) g[i] = d[i]; f = (f + f.inv(w) * g) / 2; f.resize(w); } f.resize(h.size()); return f; } arbitrary_module_poly log(const arbitrary_module_poly &h) { arbitrary_module_poly f(h); for (int i = 1; i < (int)f.size(); ++i) f[i - 1] = (ll)f[i] * i % f.mod; f[f.size() - 1] = 0, f = f * h.inv(), f.resize(h.size()); for (int i = (int)f.size() - 1; i > 0; --i) f[i] = (ll)f[i - 1] * qpow(i, f.mod - 2, f.mod) % f.mod; f[0] = 0; return f; } typedef arbitrary_module_poly m_poly; } // namespace fstdlib ``` ## 计算几何 ``` #include <bits/stdc++.h> using namespace std; const double eps = 1e-6; int Equal(double x) { if (fabs(x) < eps) return 0; if (x < 0) return -1; else return 1; } struct Point { double x, y; Point() {} Point(double _x, double _y) { x = _x; y = _y; } void input() { scanf("%lf%lf", &x, &y); } void output() { printf("%.10lf %.10lf\n", x, y); } bool operator==(Point b) const { return Equal(x - b.x) == 0 && Equal(y - b.y) == 0; } Point operator-(const Point &b) const { return Point(x - b.x, y - b.y); } double operator^(const Point &b) const { return x * b.y - y * b.x; } double operator*(const Point &b) const { return x * b.x + y * b.y; } Point operator+(const Point &b) const { return Point(x + b.x, y + b.y); } Point operator*(const double &k) const { return Point(x * k, y * k); } Point operator/(const double &k) const { return Point(x / k, y / k); } double dis(Point p) { return hypot(x - p.x, y - p.y); } double angle() //极角排序 { double res = atan2(1.0 * y, 1.0 * x) * 180.0 / pi; if (res < -eps) res = 360.0 + res; return res; } }; bool cmp(Point a, Point b) { return a.angle() < b.angle(); } struct Line { Point s, e; Line() {} Line(Point _s, Point _e) { s = _s; e = _e; } bool pointonseg(Point p) { return Equal((p - s) ^ (e - s)) == 0 && Equal((p - s) * (p - e)) <= 0; } //something 叉积精度问题。 //计算两条线交点 Point crosspoint(Line v) { double a1 = (v.e - v.s) ^ (s - v.s); double a2 = (v.e - v.s) ^ (e - v.s); return Point((s.x * a2 - e.x * a1) / (a2 - a1), (s.y * a2 - e.y * a1) / (a2 - a1)); } }; //三维模拟退火,删除z变成二维 class MidPoint { const double eps = 1e-6; double dis[110]; struct node { double x; double y; double z; node(){}; node(double x, double y, double z) : x(x), y(y), z(z){}; double dis(const node &p) { double _x = x - p.x, _y = y - p.y, _z = z - p.z; return sqrt(_x * _x + _y * _y + _z * _z); } } p[110]; //仅仅用于模拟退火 double solve(int n) { for (int i = 1; i <= n; i++) cin >> p[i].x >> p[i].y >> p[i].z; node ans(0.0, 0.0, 0.0); for (int i = 1; i <= n; i++) { ans.x += p[i].x; ans.y += p[i].y; ans.z += p[i].z; } ans.x /= n, ans.y /= n, ans.z /= n; double mmax = 0; int ptr = 0; for (int tt = 0; tt <= 100000; tt++) { for (int i = 1; i <= n; i++) { dis[i] = ans.dis(p[i]); if (dis[i] > mmax) mmax = dis[i], ptr = i; } mmax /= 10000 * sqrt(3); double _x = p[ptr].x - ans.x; double _y = p[ptr].y - ans.y; double _z = p[ptr].z - ans.z; double v = (fabs(_x) + fabs(_y) + fabs(_z)); ans.x += mmax * _x / v; ans.y += mmax * _y / v; ans.z += mmax * _z / v; } double r = 0; for (int i = 1; i <= n; i++) { r = max(r, dis[i]); } return r; } }; //凸包裸板子 Point p[111111]; bool used[111111]; Point h[111111]; void convex(int n) { int *stk = new int[n + 1]; int tp = 0; // 初始化栈 std::sort(p + 1, p + 1 + n); // 对点进行排序 stk[++tp] = 1; for (int i = 2; i <= n; ++i) { while (tp >= 2 && ((p[stk[tp]] - p[stk[tp - 1]]) ^ (p[i] - p[stk[tp]])) <= 0) used[stk[tp--]] = 0; used[i] = 1; stk[++tp] = i; } int tmp = tp; for (int i = n - 1; i > 0; --i) if (!used[i]) { while (tp > tmp && ((p[stk[tp]] - p[stk[tp - 1]]) ^ (p[i] - p[stk[tp]])) <= 0) used[stk[tp--]] = 0; used[i] = 1; stk[++tp] = i; } for (int i = 1; i <= tp; ++i) h[i] = p[stk[i]]; int ans = tp - 1; } ``` ### 扫描线模板 ## 字符串 ### 哈希 ``` struct Hash{ int base[4], mod[4]; int tot, hash[4][maxn], pw[4][maxn]; //字符串长度,hash:记从1~i的hash值,pw:记录base^i Hash(){ tot = 0; for (int i = 1; i <= 3; i++) pw[i][0] = 1; base[1] = 233; base[2] = 19260817; base[3] = 20030714; mod[1] = 1e9 + 7; mod[2] = 1e9 + 9; mod[3] = 998244353; } void init(){ tot = 0; } void insert(int c){ tot++; for (int i = 1; i <= 3; i++) hash[i][tot] = (1LL * hash[i][tot - 1] * base[i] + c) % mod[i]; for (int i = 1; i <= 3; i++) pw[i][tot] = (1LL * pw[i][tot - 1] * base[i]) % mod[i]; } //字符串[l,r]hash值,type为第几个hash int query(int l, int r, int type){ return (hash[type][r] - (1LL * hash[type][l - 1] * pw[type][r - l + 1] % mod[type]) + mod[type]) % mod[type]; } //判断字符串u的[lu,ru]内的字符串和字符串v的[lv,rv]内的字符串是否相同 friend bool same(Hash &u, int lu, int ru, Hash &v, int lv, int rv){ if (ru - lu != rv - lv) return false; for (int i = 1; i <= 3; i++) if (u.query(lu, ru, i) != v.query(lv, rv, i)) return false; return true; } }h; ``` ### NE数组运用 ``` const int N = 1e6 + 7; //字符串长度 int Next[N]; //next 数组的多种应用 void Next_init(int len, char *str) { int i = 1, j = -1; Next[0] = -1; for (; i < len; Next[i++] = j) { while (j != -1 && str[j + 1] != str[i]) j = Next[j]; if (str[j + 1] == str[i]) j++; } for (i = 0; i < len; i++) { Next[i]++; } } void show_next(int len) { for (int i = 0; i < len; i++) { cout << i << ":" << Next[i] << endl; } } //增加最少的字母使之成为一个非平凡循环串 int min_add_to_cycle(int len, char *str) { Next_init(len, str); int L = len - Next[len - 1]; if (L < len && len % L == 0) { return 0; } else return L - len % L; } //内循环节数量 int cycle_cnt(int len, char *str) { Next_init(len, str); return len % (len - Next[len - 1]) ? 1 : len / (len - Next[len - 1]); } // 同时是母串前缀和后缀的字符串的长度 vector<int> substring_of_head_and_tail(int len, char *str) { Next_init(len, str); vector<int> res; res.clear(); int i = len - 1; for (i = Next[i]; i; i = Next[i - 1]) { if (str[i - 1] == str[len - 1]) res.push_back(i); } return res; } //前一个串的后缀和后一个串的前缀的最大匹配 int max_insert_of_two_string(char *a, char *b, int len_a, int len_b) { char *str = new char[len_a + len_b]; int len = len_a + len_b; for (int i = 0; i < len_a; i++) str[i] = a[i]; for (int i = len_a; i < len; i++) str[i] = b[i - len_a]; Next_init(len, str); int i = Next[len - 1]; for (; i >= min(len_a, len_b);) i = Next[i]; return i; } //KMP YES! //不需要注释的kmp,返回所有匹配位置,下标从1开始//KMP YES! vector<int> KMP(char *a, char *b, int len_a, int len_b) //KMP YES! { //KMP YES! Next_init(len_a, a); //KMP YES! vector<int> res; //KMP YES! res.clear(); //KMP YES! int i = 0, j = 0; //KMP YES! for (; i < len_b; i++) //KMP YES! { //KMP YES! while (j && a[j] != b[i]) //KMP YES! j = Next[j - 1]; //KMP YES! if (a[j] == b[i]) //KMP YES! j++; //KMP YES! if (j == len_a) //KMP YES! res.push_back(i - len_a + 2), j = Next[j - 1]; //KMP YES! } //KMP YES! return res; //KMP YES! } //KMP YES! const int maxn = 50 * 1000 + 100; //单词长度*单词数 const int maxlen = 2e6 + 100; //主串长度 const int maxc = 128; //树分支数 int n; int ans[1005]; //在trie上添加后缀结点 //后缀结点:x的后缀结点为非x结点y,y的路径字符串为x的路径字符串的最长后缀且y的路径字符串是单词 struct ac_au { int child[maxn][maxc]; //trie int fail[maxn]; //后缀结点 int sta[maxn]; //该路径字符串单词有无,有存编号 int cnt; //结点的个数 int root; int newnode() { for (int i = 0; i < maxc; i++) child[cnt][i] = -1; sta[cnt++] = 0; return cnt - 1; } void init() { cnt = 0; root = newnode(); } void insert(char *s, int id) //trie构建 { int len = strlen(s), p = 0; for (int i = 0; i < len; i++) { int c = s[i]; int &num = child[p][c]; if (num == -1) num = newnode(); p = num; } sta[p] = id; } void build() //后缀结点的添加 { int p = 0; queue<int> que; fail[p] = p; for (int i = 0; i < maxc; i++) { int &num = child[p][i]; if (num == -1) num = p; //为下文铺垫,和树意义无关 else { fail[num] = p; //有点多余,初始化都是0,理解算法过程有用 que.push(num); } } while (!que.empty()) { p = que.front(); que.pop(); for (int i = 0; i < maxc; i++) { int &num = child[p][i]; if (num == -1) num = child[fail[p]][i]; else { fail[num] = child[fail[p]][i]; que.push(num); } } } } void query(char *s) //解决多模式串匹配单主串问题 { int len = strlen(s), p = 0; memset(ans, 0, sizeof(ans)); for (int i = 0; i < len; i++) { int c = s[i]; p = child[p][c]; int tmp = p; while (tmp) { if (sta[tmp]) ans[sta[tmp]]++; tmp = fail[tmp]; } } } } ac; ``` ### 后缀数组 ## 用到的数学公式 ### n·1+(n-1)·2+(n-2)·3+...+2·(n-1)+1·n ans=**n(n+1)(n+2)/6** ### 连续自然数平方和公式 ans=**n(n+1)(2n+1)/6** ## 离线算法 ### CDQ 3维偏序 ``` #include<bits/stdc++.h> #define ll long long using namespace std; const int maxn=1e5+5; int sum[maxn]; int n,cnt=0; ll res=0; map<int,int> mp; struct node{ int a,b; }x[maxn],c[maxn]; bool cmp(node x,node y){ return x.a<y.a; } inline int lowbit(int x) { return x & (-x); } void add(int k, int v) { while (k <= cnt) { sum[k] += v; k += lowbit(k); } } int getsum(int k) { int ret = 0; while (k) { ret += sum[k]; k -= lowbit(k); } return ret; } void solve(int l,int r){ if (l==r) return ; int mid=(l+r)>>1; solve(l,mid); solve(mid+1,r); memcpy(c,x,sizeof(x)); for (int i=0; i<=cnt; i++){ sum[i]=0; } sort(c+l,c+mid+1,cmp); sort(c+mid+1,c+r+1,cmp); int ed=l-1; for (int i=mid+1; i<=r; i++){ while (c[ed+1].a<c[i].a && ed+1<=mid){ ed++; add(mp[c[ed].b],1); } res+=getsum(mp[c[i].b]-1); } } vector<int> cc; int main(){ scanf("%d",&n); cnt=0; for (int i=1; i<=n; i++){ scanf("%d%d",&x[i].a,&x[i].b); cc.push_back(x[i].b); } sort(cc.begin(),cc.end()); for (auto i:cc){ if (mp[i]==0) mp[i]=++cnt; } solve(1,n); printf("%lld",res); } ``` ### 莫队 #### 普通莫队 ``` #include <algorithm> #include <cmath> #include <cstdio> using namespace std; const int N = 50005; int n, m, maxn; int c[N]; long long sum; int cnt[N]; long long ans1[N], ans2[N]; struct query { int l, r, id; bool operator<(const query &x) const { if (l / maxn != x.l / maxn) return l < x.l; return (l / maxn) & 1 ? r < x.r : r > x.r; } } a[N]; void add(int i) { sum += cnt[i]; cnt[i]++; } void del(int i) { cnt[i]--; sum -= cnt[i]; } long long gcd(long long a, long long b) { return b ? gcd(b, a % b) : a; } int main() { scanf("%d%d", &n, &m); maxn = sqrt(n); for (int i = 1; i <= n; i++) scanf("%d", &c[i]); for (int i = 0; i < m; i++) scanf("%d%d", &a[i].l, &a[i].r), a[i].id = i; sort(a, a + m); for (int i = 0, l = 1, r = 0; i < m; i++) { if (a[i].l == a[i].r) { ans1[a[i].id] = 0, ans2[a[i].id] = 1; continue; } while (l > a[i].l) add(c[--l]); while (r < a[i].r) add(c[++r]); while (l < a[i].l) del(c[l++]); while (r > a[i].r) del(c[r--]); ans1[a[i].id] = sum; ans2[a[i].id] = (long long)(r - l + 1) * (r - l) / 2; } for (int i = 0; i < m; i++) { if (ans1[i] != 0) { long long g = gcd(ans1[i], ans2[i]); ans1[i] /= g, ans2[i] /= g; } else ans2[i] = 1; printf("%lld/%lld\n", ans1[i], ans2[i]); } return 0; } ``` #### 带修改莫队 ``` #include <bits/stdc++.h> #define SZ (10005) using namespace std; template <typename _Tp> inline void IN(_Tp& dig) { char c; dig = 0; while (c = getchar(), !isdigit(c)) ; while (isdigit(c)) dig = dig * 10 + c - '0', c = getchar(); } int n, m, sqn, c[SZ], ct[SZ], c1, c2, mem[SZ][3], ans, tot[1000005], nal[SZ]; struct query { int l, r, i, c; bool operator<(const query another) const { if (l / sqn == another.l / sqn) { if (r / sqn == another.r / sqn) return i < another.i; return r < another.r; } return l < another.l; } } Q[SZ]; void add(int a) { if (!tot[a]) ans++; tot[a]++; } void del(int a) { tot[a]--; if (!tot[a]) ans--; } char opt[10]; int main() { IN(n), IN(m), sqn = pow(n, (double)2 / (double)3); for (int i = 1; i <= n; i++) IN(c[i]), ct[i] = c[i]; for (int i = 1, a, b; i <= m; i++) if (scanf("%s", opt), IN(a), IN(b), opt[0] == 'Q') Q[c1].l = a, Q[c1].r = b, Q[c1].i = c1, Q[c1].c = c2, c1++; else mem[c2][0] = a, mem[c2][1] = ct[a], mem[c2][2] = ct[a] = b, c2++; sort(Q, Q + c1), add(c[1]); int l = 1, r = 1, lst = 0; for (int i = 0; i < c1; i++) { for (; lst < Q[i].c; lst++) { if (l <= mem[lst][0] && mem[lst][0] <= r) del(mem[lst][1]), add(mem[lst][2]); c[mem[lst][0]] = mem[lst][2]; } for (; lst > Q[i].c; lst--) { if (l <= mem[lst - 1][0] && mem[lst - 1][0] <= r) del(mem[lst - 1][2]), add(mem[lst - 1][1]); c[mem[lst - 1][0]] = mem[lst - 1][1]; } for (++r; r <= Q[i].r; r++) add(c[r]); for (--r; r > Q[i].r; r--) del(c[r]); for (--l; l >= Q[i].l; l--) add(c[l]); for (++l; l < Q[i].l; l++) del(c[l]); nal[Q[i].i] = ans; } for (int i = 0; i < c1; i++) printf("%d\n", nal[i]); return 0; } ``` #### 回滚莫队 ``` #include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e5 + 5; int n, q; int x[N], t[N], m; struct Query { int l, r, id; } Q[N]; int pos[N], L[N], R[N], sz, tot; int cnt[N], __cnt[N]; ll ans[N]; inline bool cmp(const Query& A, const Query& B) { if (pos[A.l] == pos[B.l]) return A.r < B.r; return pos[A.l] < pos[B.l]; } void build() { sz = sqrt(n); tot = n / sz; for (int i = 1; i <= tot; i++) { L[i] = (i - 1) * sz + 1; R[i] = i * sz; } if (R[tot] < n) { ++tot; L[tot] = R[tot - 1] + 1; R[tot] = n; } } inline void Add(int v, ll& Ans) { ++cnt[v]; Ans = max(Ans, 1LL * cnt[v] * t[v]); } inline void Del(int v) { --cnt[v]; } int main() { scanf("%d %d", &n, &q); for (int i = 1; i <= n; i++) scanf("%d", &x[i]), t[++m] = x[i]; for (int i = 1; i <= q; i++) scanf("%d %d", &Q[i].l, &Q[i].r), Q[i].id = i; build(); // 对询问进行排序 for (int i = 1; i <= tot; i++) for (int j = L[i]; j <= R[i]; j++) pos[j] = i; sort(Q + 1, Q + 1 + q, cmp); // 离散化 sort(t + 1, t + 1 + m); m = unique(t + 1, t + 1 + m) - (t + 1); for (int i = 1; i <= n; i++) x[i] = lower_bound(t + 1, t + 1 + m, x[i]) - t; int l = 1, r = 0, last_block = 0, __l; ll Ans = 0, tmp; for (int i = 1; i <= q; i++) { // 询问的左右端点同属于一个块则暴力扫描回答 if (pos[Q[i].l] == pos[Q[i].r]) { for (int j = Q[i].l; j <= Q[i].r; j++) ++__cnt[x[j]]; for (int j = Q[i].l; j <= Q[i].r; j++) ans[Q[i].id] = max(ans[Q[i].id], 1LL * t[x[j]] * __cnt[x[j]]); for (int j = Q[i].l; j <= Q[i].r; j++) --__cnt[x[j]]; continue; } // 访问到了新的块则重新初始化莫队区间 if (pos[Q[i].l] != last_block) { while (r > R[pos[Q[i].l]]) Del(x[r]), --r; while (l < R[pos[Q[i].l]] + 1) Del(x[l]), ++l; Ans = 0; last_block = pos[Q[i].l]; } // 扩展右端点 while (r < Q[i].r) ++r, Add(x[r], Ans); __l = l; tmp = Ans; // 扩展左端点 while (__l > Q[i].l) --__l, Add(x[__l], tmp); ans[Q[i].id] = tmp; // 回滚 while (__l < l) Del(x[__l]), ++__l; } for (int i = 1; i <= q; i++) printf("%lld\n", ans[i]); return 0; } ``` ### 整体二分 ``` #include <bits/stdc++.h> using namespace std; const int N = 200020; const int INF = 1e9; int n, m; int ans[N]; // BIT begin int t[N]; int a[N]; int sum(int p) { int ans = 0; while (p) { ans += t[p]; p -= p & (-p); } return ans; } void add(int p, int x) { while (p <= n) { t[p] += x; p += p & (-p); } } // BIT end int tot = 0; struct Query { int l, r, k, id, type; // set values to -1 when they are not used! } q[N * 2], q1[N * 2], q2[N * 2]; void solve(int l, int r, int ql, int qr) { if (ql > qr) return; if (l == r) { for (int i = ql; i <= qr; i++) if (q[i].type == 2) ans[q[i].id] = l; return; } int mid = (l + r) / 2, cnt1 = 0, cnt2 = 0; for (int i = ql; i <= qr; i++) { if (q[i].type == 1) { if (q[i].l <= mid) { add(q[i].id, 1); q1[++cnt1] = q[i]; } else q2[++cnt2] = q[i]; } else { int x = sum(q[i].r) - sum(q[i].l - 1); if (q[i].k <= x) q1[++cnt1] = q[i]; else { q[i].k -= x; q2[++cnt2] = q[i]; } } } // rollback changes for (int i = 1; i <= cnt1; i++) if (q1[i].type == 1) add(q1[i].id, -1); // move them to the main array for (int i = 1; i <= cnt1; i++) q[i + ql - 1] = q1[i]; for (int i = 1; i <= cnt2; i++) q[i + cnt1 + ql - 1] = q2[i]; solve(l, mid, ql, cnt1 + ql - 1); solve(mid + 1, r, cnt1 + ql, qr); } pair<int, int> b[N]; int toRaw[N]; int main() { scanf("%d%d", &n, &m); // read and discrete input data for (int i = 1; i <= n; i++) { int x; scanf("%d", &x); b[i].first = x; b[i].second = i; } sort(b + 1, b + n + 1); int cnt = 0; for (int i = 1; i <= n; i++) { if (b[i].first != b[i - 1].first) cnt++; a[b[i].second] = cnt; toRaw[cnt] = b[i].first; } for (int i = 1; i <= n; i++) { q[++tot] = {a[i], -1, -1, i, 1}; } for (int i = 1; i <= m; i++) { int l, r, k; scanf("%d%d%d", &l, &r, &k); q[++tot] = {l, r, k, i, 2}; } solve(0, cnt + 1, 1, tot); for (int i = 1; i <= m; i++) printf("%d\n", toRaw[ans[i]]); } ``` ## 矩阵 ``` #include <bits/stdc++.h> using namespace std; typedef long long ll; const int mod = (int)1e9 + 7; struct matrix { const static int N = 101; int e[N][N], n, m; matrix() {} matrix(int _n, int _m) : n(_n), m(_m) { for (int i = 0; i < n; i++) memset(e[i], 0, sizeof(int) * N); } matrix operator*(const matrix &rhs) { matrix ret(n, rhs.m); for (int i = 0; i < n; i++) for (int j = 0; j < rhs.m; j++) for (int z = 0; z < m; z++) ret.e[i][j] = (ret.e[i][j] + 1ll * e[i][z] * rhs.e[z][j]) % mod; return ret; } matrix operator+(const matrix &rhs) { matrix ret(n, m); for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) ret.e[i][j] = (e[i][j] + rhs.e[i][j]) % mod; return ret; } matrix operator-(const matrix &rhs) { matrix ret(n, m); for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) ret.e[i][j] = (e[i][j] - rhs.e[i][j] + mod) % mod; return ret; } matrix qpow(ll b) { matrix a = (*this), ret(n, n); for (int i = 0; i < n; i++) ret.e[i][i] = 1; while (b) { if (b & 1) ret = ret * a; a = a * a; b >>= 1; } return ret; } }; ``` ## 公式&定理 ![](https://i.imgur.com/XEtnA8m.png)

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully