## 前言
之前的有在其他被廢所以現在我險一個新的,參考IO wiki
# 線段樹
```cpp=
//from:https://oi-wiki.org/ds/seg/
#include <bits/stdc++.h>
using namespace std;
template <typename T>
class SegTree{
vector<T>tree,lazy;
vector<T>*arr;
int n,root,n4,end;
void maintain(int cl,int cr,int p){
int cm=cl+(cr-cl)/2;
if (cl!=cr&&lazy[p]){
lazy[p*2]+=lazy[p];
lazy[p*2+1]+=lazy[p];
tree[p*2]+=lazy[p]*(cm-cl+1);
tree[p*2+1]+=lazy[p]*(cr-cm);
lazy[p]=0;
}
}
T range_sum(int l,int r,int cl,int cr,int p){//區間總和
if(l<=cl&&cr<=r)return tree[p];
int m=cl+(cr-cl)/2;
T sum=0;
maintain(cl,cr,p);
if(l<=m)sum+=range_sum(l,r,cl,m,p*2);
if(r>m)sum+=range_sum(l,r,m+1,cr,p*2+1);
return sum;
}
void range_set(int l,int r,T val,int cl,int cr,int p){//區間加值
if (l<=cl&&cr<=r){
lazy[p]=val;
tree[p]=(cr-cl+1)*val;
return;
}
int m=cl+(cr-cl)/2;
maintain(cl, cr, p);
if(l<=m)range_set(l,r,val,cl,m,p*2);
if(r> m)range_set(l,r,val,m+1,cr,p*2+1);
tree[p]=tree[p*2]+tree[p*2+1];
}
void range_add(int l,int r,T val,int cl,int cr,int p) {//區間設值
if(l<=cl&&cr<=r){
lazy[p]+=val;
tree[p]+=(cr-cl+1)*val;
return;
}
int m=cl+(cr-cl)/2;
maintain(cl,cr,p);
if(l<=m)range_add(l,r,val,cl,m,p*2);
if(r> m)range_add(l,r,val,m+1,cr,p*2+1);
tree[p]=tree[p*2]+tree[p*2+1];
}
void build(int s, int t, int p) {//建樹
if(s==t){
tree[p]=(*arr)[s];
return;
}
int m=s+(t-s)/2;
build(s,m,p*2);
build(m+1,t,p*2+1);
tree[p]=tree[p*2]+tree[p*2+1];
}
public:
explicit SegTree<T>(vector<T> v) {
n=v.size();
n4=n*4;
tree=vector<T>(n4,0);
lazy=vector<T>(n4,0);
arr=&v;
end=n-1;
root=1;
build(0,end,1);
arr=nullptr;
}
void show(int p, int depth=0){
if(p>n4||tree[p]==0)return;
show(p*2,depth+1);
for(int i=0;i<depth;i++)putchar('\t');
printf("%d:%d\n", tree[p],lazy[p]);
show(p*2+1,depth+1);
}
T range_sum(int l, int r) {return range_sum(l, r,0,end,root); }
void range_add(int l, int r,int val){range_add(l,r,val,0,end,root);}
void range_set(int l, int r,int val){range_set(l,r,val,0,end,root);}
};
int main(){
int n,m,t,x,y,k;
cin>>n>>m;
vector<long long>a(n);
for(auto&i:a)cin>>i;
SegTree<long long>s(a);
//s.show(1);//debuge用
while(m--){
cin>>t;
if(t==1){//區間加值
cin>>x>>y>>k;
s.range_add(x-1,y-1,k);
}
else {//區間改值
cin>>x>>y;
cout<<s.range_sum(x-1,y-1)<<endl;
}
}
}
```
# 最短路
```cpp=
#include<bits/stdc++.h>
#define inf 0xfffff
using namespace std;
int main(){
int t,n,r;cin>>t;
while(t--){
cin>>n>>r;
int Map[n][r],ans[n][r],d[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
bool vis[n][r]={};
for(auto&i:Map)for(auto&j:i)cin>>j;
for(auto&i:ans)for(auto&j:i)j=inf;ans[0][0]=Map[0][0];
priority_queue<tuple<int,int,int>>pq;
pq.push(make_tuple(-Map[0][0],0,0));
//dijkstra
while(pq.size()){
int val=-get<0>(pq.top()),x=get<1>(pq.top()),y=get<2>(pq.top());
pq.pop();
vis[x][y]=1;
for(int i=0;i<4;i++){
int nx =x+d[i][0],ny=y+d[i][1];
if(nx>=0&&nx<n&&ny>=0&&ny<r&&!vis[nx][ny]){
int tmp=val+Map[nx][ny];
if(ans[nx][ny]>tmp){
ans[nx][ny]=tmp;
pq.push(make_tuple(-tmp,nx,ny));
}
}
}
}
cout<<ans[n-1][r-1]<<"\n";
}
}
```
# 併查集
最小生成樹演算法 中的 Kruskal 和 最近公共祖先 中的 Tarjan 演算法是基於併查集的演算法。
```cpp=
#include <bits/stdc++.h>
using namespace std;
struct dsu {
vector<size_t>pa,size,sum;//unsigned int
explicit dsu(size_t size_)
:pa(size_*2),size(size_*2,1),sum(size_*2){
//size 與 sum 的前半段其實沒有使用,只是為了讓下標計算更簡單
iota(pa.begin(),pa.begin()+size_, size_);//生成連續數
iota(pa.begin()+size_,pa.end(),size_);
iota(sum.begin()+size_,sum.end(),0);
}
void unite(size_t x, size_t y){//合併
x=find(x),y=find(y);
if(x==y)return;
if(size[x]<size[y])swap(x,y);
pa[y]=x;
size[x]+=size[y];
sum[x]+=sum[y];
}
void move(size_t x,size_t y){
//將x從他所在的群體當中移除並且加入y所在的群體
auto fx=find(x),fy=find(y);
if(fx==fy)return;
pa[x]=fy;
--size[fx],++size[fy];
sum[fx]-=x,sum[fy]+=x;
}
size_t find(size_t x){return pa[x]==x?x:pa[x]=find(pa[x]); }
};
int main() {
size_t n,m,op,x,y;
while(cin>>n>>m){
dsu dsu(n+1);//元素范围是1..n
while(m--){
cin>>op;
switch(op){
case 1:
cin>>x>>y;
dsu.unite(x,y);
break;
case 2:
cin>>x>>y;
dsu.move(x,y);
break;
case 3:
cin>>x;
x=dsu.find(x);
cout<<dsu.size[x]<<' '<<dsu.sum[x]<<'\n';
break;
default:assert(false);
}
}
}
}
/*
5 4 1~5個數 4個指令
1 1 2 指令"1" 1 2 合併
2 3 4 指令"2" 將3從他所在的群體當中移除並且加入4所在的群體
3 4 指令"3" 印出4所在的群體的size大小跟種數
*/
```
# 拓樸
```cpp=
#include"bits/stdc++.h"
using namespace std;
int main(){
int m,p,a,b,temp;
while(cin>>m>>p&&m+p){
vector<int>id(m,0);//indegree
vector<int>edge[m];
while(p--){
cin>>a>>b;
edge[a-1].push_back(b-1);
id[b-1]++;
}
queue<int>q;
for(int i=0;i<m;i++)if(!id[i])q.push(i);
bool flag=0;
while(q.size()){
if(flag++)cout<<' ';
temp=q.front();
q.pop();
cout<<temp+1;
for(auto&i:edge[temp]){
id[i]--;
if(!id[i])q.push(i);
}
}
cout<<endl;
}
}
```
## 字典樹
```cpp=
struct TrieNode {
int nxt[26]; //遇到a~z時的節點編號(idx)
int cnt; //以此結尾的字串個數
} node[1000005];
int idx = 2; //root = 1
void insert(string s){
int cur = 1; //從root開始
for(auto i:s) {
if(node[cur].nxt[i-'a'] == 0){
node[cur].nxt[i-'a']=idx;//開一個新的node
cur = idx;
idx++;
}
else {
cur = node[cur].nxt[i-'a'];
}
}
node[cur].cnt++;
}
int query(string s){
int cur = 1;
for(auto i:s)
if(node[cur].nxt[i-’a’] == 0)return 0;
else cur = node[cur].nxt[i-’a’];
return node[cur].cnt;
}
```
# LCS&LIS&LPS
## LCS 和 LIS 題目轉換
- LIS 轉成 LCS
1. A 為原序列,B=sort(A)
2. 對 A,B 做 LCS
- LCS 轉成 LIS
1. A,B 為原本的兩序列
2. 最 A 序列作編號轉換,將轉換規則套用在 B
3. 對 B 做 LIS
4. 重複的數字在編號轉換時後要變成不同的數字,越早出現的數字要越小
5. 如果有數字在 B 裡面而不在 A 裡面,直接忽略這個數字不做轉換即可
```cpp=
// 為了實作方便,從陣列的第1格開始存入序列。
int length[N1+1][N2+1];// DP表格
int LCS(){
// 初始值:當s1或s2是空集合,則LCS是空集合。
for (int i=0; i<=N1; i++) length[i][0] = 0;
for (int j=0; j<=N2; j++) length[0][j] = 0;
// 填表格:依照遞迴公式
for (int i=1; i<=N1; i++)
for (int j=1; j<=N2; j++)
if (s1[i] == s2[j])
length[i][j] = length[i-1][j-1] + 1;
else
length[i][j] = max(length[i-1][j],length[i][j-1]);
return length[N1][N2];
}
```
```cpp=
//LIS長度(順序是錯的)運用dp+greedy+binary search
//https://www.youtube.com/watch?v=l2rCz7skAlk 講解
//O(nlogn)
#include"bits/stdc++.h"
using namespace std;
int LIS(auto&a){
vector<int>dp;
for(auto&i:a)
if(dp.empty()||dp.back()<i)dp.push_back(i);
else{auto it=lower_bound(dp.begin(),dp.end(),i);*it=i;}
return dp.size();
}
int main(){
int n;vector<int>a;
while(cin>>n)a.push_back(n);
cout<<LIS(a);
}
```
```cpp=
//LPS 最長回文子序列
//https://www.youtube.com/watch?v=g3R-pjUNa3k
#include"bits/stdc++.h"
using namespace std;
string LPS(string s){
int n=s.size();
auto getlen=[&](int l,int r){
while(l>=0&&r<n&&s[l]==s[r]){
r++;
l--;
}
return r-l-1;
};
int len=0;
int start=0;
for(int i=0;i<n;i++){
int temp=max(getlen(i,i),getlen(i,i+1));
if(temp>len){
len=temp;
start=i-(len-1)/2;
}
}
return s.substr(start,len);
}
int main(){
string s;
cin>>s;
cout<<LPS(s);
}
```
# 擴增歐基里德
```cpp!
#include<iostream>
using namespace std;
int gcd(int a,int b,int&x,int&y){//g=ax+by
if(b==0){
x=1;y=0;return a;
}
int ox,oy;
int g=gcd(b,a%b,ox,oy);
x = oy;
y = ox-a/b*oy;
return g;
}
int main(){
int a,b;cin>>a;
while(cin>>a>>b){
int x,y,g=gcd(a,b,x,y);
printf("%d %d %d\n",x,y,g);
}
}
```
# 超大數相乘
```java!
import java.math.BigInteger;
import java.util.Scanner;
class Main{
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
while(sc.hasNext()){
BigInteger n1=sc.nextBigInteger();
BigInteger n2=sc.nextBigInteger();
System.out.println(n1.multiply(n2));
}
}
};
```