ACM板子大公开!
目前只有非常少的一部分,正在逐渐完善中...
基础
归并排序求逆序对
int merge_sort (int l, int r) {
if (l >= r) return 0;
int mid = l + r >> 1;
int res = merge_sort (l, mid) + merge_sort (mid + 1, r);
int k = 0, i = l, j = mid + 1; //计数,左半段起点,右半段起点
while (i <= mid && j <= r) {
if (a[i] <= a[j]) tmp[k ++] = a[i ++]; //有序
else {
tmp[k ++] = a[j ++];
res += mid - i + 1; //夹着的那段就是逆序对的数量
}
}
while (i <= mid) tmp[k ++] = a[i ++];
while (j <= r) tmp[k ++] = a[j ++];
for (int i = l, j = 0; i <= r; i ++, j ++) a[i] = tmp[j];
return res;
}
高精度
高精加
vector<int> add(vector<int>& A, vector<int>& B) {
if (A.size() < B.size()) return add(B, A);//大的放前面
vector<int>C;
int t = 0;
for (int i = 0; i < A.size(); i++) {
t += A[i];
if (i < B.size()) t += B[i];
C.push_back(t % 10);
t /= 10;
}
if (t) C.push_back(t);
return C;
}
高精减
bool cmp(vector<int> &A, vector<int> &B) { //先 比较,然后大减小
if (A.size() != B.size()) return A.size() > B.size();
for (int i = A.size() - 1; i >= 0; i -- ) {
if (A[i] != B[i]) return A[i] > B[i];
}
return true;
}
vector<int> sub(vector<int> &A, vector<int> &B) {
vector<int> C;
for (int i = 0, t = 0; i < A.size(); i ++ ) {
t = A[i] - t;
if (i < B.size()) t -= B[i];
C.push_back((t + 10) % 10);
if (t < 0) t = 1;
else t = 0;
}
while (C.size() > 1 && C.back() == 0) C.pop_back();
return C;
}
高精乘
vector<int> mult(vector<int>& A, int b) {
t = 0;
for (int i = 0; i < A.size()||t; i++) {
if (i < A.size())
t += A[i]*b;
C.push_back(t % 10);
t /= 10;
}
if (C.size() > 1 && C.back() == 0)
C.pop_back();
return C;
}
高精除
vector<int> div(vector<int>& A, int b, int &r) {
r = 0;
for (int i = A.size()-1; i >= 0 ; i--) {
r = r * 10 + A[i];
C.push_back(r / b);
r %= b;
}
reverse(C.begin(), C.end());
while (C.size() > 1 && C.back() == 0)
C.pop_back();
return C;
}
滑动窗口
for (int i = 1; i <= n; i ++){ //szie=k
while (!q.empty() && i - k >= q.front()) q.pop_front();
while (!q.empty() && a[q.back()] >= a[i]) q.pop_back();
q.push_back(i);
if (i >= k) cout << a[q.front()] <<' ';
}
dp
背包dp
01背包
for(int i = 1; i <= n; i ++)
for(int j = V; j >= v[i]; j --){
dp[j] = max(dp[j], dp[j-v[i]]+w[i]);
}
/*
//求具体方案
for (int i = n; i >= 1; i --)
for (int j = 0; j <= m; j ++) {
f[i][j] = f[i + 1][j];
if (j >= v[i])
f[i][j] = max (f[i + 1][j], f[i + 1][j - v[i]] + w[i]);
}
int j = m, cnt = 0;
for (int i = 1; i <= n; i ++) {
if (j >= v[i] && f[i][j] == f[i + 1][j - v[i]] + w[i])
cout << i << ' ', j -= v[i];
}
*/
完全背包
for(int i = 1; i <= n; i ++)
for(int j = v[i]; j <= V; j ++){
dp[j] = max(dp[j], dp[j-v[i]]+w[i]);
}
多重背包
for(int i = 1; i <= n; i ++){
cin >> a >> b >> s;
int k = 1;
while(k <= s){
cnt ++;//别忘了cnt
v[cnt] = a * k;
w[cnt] = b * k;
s -= k;//一定要先剪掉啊
k *= 2;//顺序反了就寄掉
}
if(s){
cnt ++;//打包数
v[cnt] = a * s;
w[cnt] = b * s;//小心变量cnt
}//先加后用
}
n = cnt;//用打好包的来代替n,后面就可以实现01背包了
分组背包
for(int i =1; i <=n; i ++)
for(int j = V; j >= 0; j --)//不只是这一组,不能直接把边界设置在这里
for(int k = 0; k < s[i]; k ++){
if(j >= v[i][k])//判断条件转移到这里
dp[j] = max(dp[j], dp[j - v[i][k]] + w[i][k]);
}//以k为组
区间dp
for(int len = 2; len <= n; len ++)
for(int i = 1; i <= n - len + 1; i ++){
int l = i, r = i + len - 1;
dp[l][r] = INF;
for(int k = l ; k < r; k ++)
dp[l][r] = min(dp[l][r], dp[l][k] + dp[k+1][r] + s[r] - s[l-1]);
}
树形dp
void dfs (int u) {
f[u][0] = 0;
f[u][1] = w[u];
for (auto v : e[u]) {
dfs (v);
f[u][0] += max (f[v][1], f[v][0]);
f[u][1] += f[v][0];
}
}
期望dp
dag上期望dp
double cal () {
for (int i = 1; i <= n; i++) f[i] = 0.0;
for (int i = n; i >= 1; i--) {
//f[i] = 1;
int fm = v[i].size (); //等概率
double maxn = 0;
for (auto j : v[i]) {
double tt = f[j.first] + j.second; //vector存图{终点,距离}
maxn = max (maxn, tt);
f[i] += tt / fm;
}
}
return f[1];
}
数位dp
ll f[N][10], n; //f[i][j]:长度为i,余数为j
ll dfs(int pos,int limit, int mod){
if(!pos) {
if (mod) return 0;
else return 1;
}
if(!limit && f[pos][mod] != -1) return f[pos][mod];
ll res = 0, up = limit ? a[pos] : 9;
for(int i = 0; i <= up;i++) {
if(i == 4) continue;
res += dfs(pos - 1,limit && i == up, (mod * 10 + i) % 7);
}
return limit ? res : f[pos][mod] = res;
}
ll dp(ll n) {
memset(f, -1, sizeof f);
int len = 0;
while(n) a[++len] = n % 10, n /= 10;
return dfs(len ,1, 0) - 1;
}
数学
快速幂
ll qmi(ll a, ll k, ll p){
ll res = 1;
while(k){
if(k & 1)
res = (ll)res * a % p;
a = (ll)a * a % p;
k >>= 1;
}
return res;
}
取模的时候一定要加到正数为止
数学小结论
线性筛
void get_prime(int n){
for(int i = 2; i <= n; i ++){
if(!vis[i]) primes[cnt++] = i;
for(int j = 0; primes[j] <= n / i; j ++){
vis[primes[j] * i] = true;
if(i % primes[j] == 0) break;
}
}
}
求逆元
p为质数
费马小定理:qmi(x,p-2,p)
线性递推:
inv[1] = 1;
for (int i = 2; i <= n; i++) {
inv[i] = (p - p / i) * inv[p % i] % p;
}
p不为质数
扩欧
ll x = 0, y = 0;
int d = exgcd(a, p, x, y);
if (d == 1) cout << (1ll * x + p) % p << endl;
else puts("impossible");
扩展欧几里得
ll exgcd(ll a, ll b, ll &x, ll &y) {
if (b == 0) {
x = 1;
y = 0;
return a;
}
ll d = exgcd(b, a % b, y, x);
y -= (a / b) * x;
return d;
}
int gcd(int a, int b) {
return b ? gcd(b, a % b) : a;
}
线性同余方程
解线性同余方程
// 求 a * x = b (mod m) 的解
ll modequ(ll a, ll b, ll m) {
ll x, y;
ll d = exgcd(a, m, x, y);
if (b % d != 0) return -1;
m /= d; a /= d; b /= d;
x = x * b % m;
if (x < 0) x += m;
return x;
}
合并线性同余方程 EXCRT
// 合并两个同余方程
void merge(ll &a, ll &b, ll c, ll d) { // d <= 10^9
// bt = c - a(mod d)
if (a == -1 && b == -1) return;
ll x, y;
ll g = exgcd(b, d, x, y);
//bx = g(mod d)
if ((c - a) % g != 0) {
a = b = -1;
return;
}
d /= g; // d'
ll t0 = ((c - a) / g) % d * x % d;
if (t0 < 0) t0 += d;
// t = t0 (mod d')
a = b * t0 + a;
b = b * d;
}
欧拉函数
ll phi (ll n) {
ll ans = n;
for (int i = 2; i*i <= n; i++) {
if (n % i == 0) {
ans = ans / i * (i - 1); //*(1-1/i)
while (n % i == 0) n /= i;
}
}
if (n > 1) ans = ans / n * (n - 1); //还剩下就再乘
return ans;
}
筛法求欧拉
ll get_prime(int x) {
phi[1] = 1;
for (int i = 2; i <= n; i++) {
if (!vis[i]) primes[cnt++] = i, phi[i] = i - 1;
for (int j = 0; primes[j]<= x / i; j++) {
vis[i * primes[j]] = true;
if (i % primes[j] == 0) {
phi[primes[j] * i] = phi[i] * primes[j];
break;
}
phi[primes[j] * i] = phi[i] * (primes[j] - 1);
}
}
ll ans = 0;
for (int i = 1; i <= n; i++) ans += phi[i];
return ans;
}
求组合数
二项式定理:
ll fact[N], infact[N];
ll qmi(ll a, ll k, ll p){
ll res = 1;
while(k){
if(k & 1)
res = (ll)res * a % p;
a = (ll)a * a % p;
k >>= 1;
}
return res;
}
ll C (int a, int b) {
if (a < b) return 0;
return fact[a] * infact[b] % mod * infact[a - b] % mod;
}
void init () {
fact[0] = infact[0] = 1;
for(int i = 1; i < N; i++){
fact[i] = (ll)fact[i-1] * i % mod;
infact[i] = (ll)infact[i-1] * qmi(i, mod - 2,mod) % mod;
}
}
线性筛法求Mobius函数
void init (int x) { //线性筛法求mobius函数
mobius[1] = 1;
int cnt = 0;
for (int i = 2; i <= x; i++) {
if (!vis[i]) prime[++cnt] = i, mobius[i] = -1;
for (int j = 1; i * prime[j] <= x; j++) {
int t = i * prime[j];
vis[t] = true;
if (i % prime[j] == 0) {
mobius[t] = 0;
break;
}
mobius[t] = mobius[i] * -1;
}
}
for (int i = 1; i <= x; i++) sum[i] = sum[i-1] + mobius[i];
}
线性基
struct linear_basis {
ll num[63];
bool insert (ll x) {
for (int i = 62; i >= 0; i--) {
if ((x >> i & 1) == 0) continue;
if (num[i] == 0) {
num[i] = x;
return true;
}
else x ^= num[i];
}
return false;
}
ll querymin (ll x) {
for (int i = 62; i >= 0; i --) {
x = min (x, x ^ num[i]);
}
return x;
}
ll querymax (ll x) {
for (int i = 62; i >= 0; i --) {
x = max (x, x ^ num[i]);
}
return x;
}
}T;
字符串
KMP
void Next(string &p, vi &ne){
for (int i = 1, j = 0; i < n; i ++){
while (j && p[i] != p[j])
j = ne[j - 1];
if (p[i] == p[j])
j ++;
ne[i] = j;
}
}
void KMP (string &s, string &p, int begin){
vi ne(n);
Next (p, ne);
for (int i = begin, j = 0; i < m; i ++){
while (j && s[i] != p[j])
j = ne[j - 1];
if (s[i] == p[j])
j ++;
if (j == n){
cout << i - n + 1 << ' ';
j = ne[j - 1];
}
}
}
Z函数(扩展KMP)
vector<int> ExKMP (string s) { //从0开始
int n = s.size ();
vector<int> z(n);
for (int i = 1, l = 0, r = 0; i < n; i++) {
if (i <= r && z[i-l] < r - i + 1) z[i] = z[i-l];
else {
z[i] = max (0, r - i + 1);
while (i + z[i] < n && s[z[i]] == s[i + z[i]]) z[i] ++; //暴力匹配
}
if (i + z[i] > r) l = i, r = i + z[i] - 1; //更新l,r
}
return z;
}
Trie
void insert(char *str){
int p = 0;
for (int i = 0; str[i]; i ++){
int u = str[i] - 'a';
if (!son[p][u]) son[p][u] = ++idx;
p = son[p][u];
}
cnt[p]++;
}
int query(char *str){
int p = 0;
for (int i = 0; str[i]; i ++){
int u = str[i] - 'a';
if (!son[p][u]) return 0;
p = son[p][u];
}
return cnt[p];
}
最大异或对(01Trie)
void insert(int x){
int p = 0;
for (int i = 30; i >= 0; i --){//从高到低
int t = x >> i & 1;//取出i位
if (!son[p][t])
son[p][t] = ++ idx;
p = son[p][t];
}
}
int query(int x){
int p = 0, res = 0;
for (int i = 30; i >= 0; i --){
int t = x >> i & 1;
if (son[p][!t]){//不同数
res += 1 << i;//局部值
p = son[p][!t];
}
else
p = son[p][t];//找下一个
}
return res;
}
最小表示法
int find (char* s){ //原s复制一倍之后
int i = 0, j = 1;
while (i < n && j < n){
int k = 0;
while (k < n && s[i + k] == s[j + k]) k ++;
if (k == n) break;
if (s[i + k] > s[j + k]) i += k + 1;
else j += k + 1;
if (i == j) j ++;//不能走到相同的地方
}
int k = min (i, j);
s[k + n] = 0;
return k; //最小表示法的起点
}
字符串哈希
双哈希
const int N = 2e6 + 5; //两倍n
const int mod1 = 1e9 + 7, mod2 = 1e9 + 9;
struct pii {
int p1, p2;
pii () {}
pii (int p1_, int p2_) : p1(p1_), p2(p2_) {}
pii operator + (pii t) {
t.p1 = (this->p1 + t.p1) % mod1;
t.p2 = (this->p2 + t.p2) % mod2;
return t;
}
pii operator - (pii t) {
t.p1 = (this->p1 - t.p1 + mod1) % mod1;
t.p2 = (this->p2 - t.p2 + mod2) % mod2;
return t;
}
pii operator * (pii t) {
t.p1 = (this->p1 * t.p1) % mod1;
t.p2 = (this->p2 * t.p2) % mod2;
return t;
}
bool operator == (pii t) {
if (t.p1 == this->p1 && t.p2 == this->p2) return true;
return false;
}
}p[N], pre[N], suf[N]; //pow值, 前缀哈希和, 后缀哈希和(左闭右开)
signed main() {
int n;
string t;
cin >> n >> t;
pii P = {131, 13331}; //进制
p[0] = {1, 1};
for (int i = 1; i <= 2 * n; i++) {
p[i] = p[i-1] * P;
pii tt = {t[i-1] - 'a', t[i-1] - 'a'};
pre[i] = pre[i-1] * P + tt;
}
for (int i = n * 2; i; i--) {
pii tt = {t[i-1] - 'a', t[i-1] - 'a'};
suf[i] = suf[i+1] * P + tt;
}
//后略
}
图论
杂
树的重心
将这个点删除后,剩余各个连通块中点数的最大值最小
int dfs(int x){
int sum = 1, ans = 0;
vis[x] = true;
for (int i = h[x]; i != -1; i = ne[i]){
int j = e[i];
if (!vis[j]){
int s = dfs(j);
ans = max(ans, s);
sum += s;
}
}
ans = max (ans, n - sum);//不含自身
res = min (res, ans);//每一个点当中取最小的那个
return sum;
}//返回以x为根的子数中的点数
拓扑排序
bool topsort(){
queue<int> q;
for(int i = 1;i <= n; i++) if(d[i] == 0) q.push(i);
while(q.size()){
int t = q.front();
top[cnt++] = t;
q.pop();
for(int i = h[t];i != -1; i = ne[i]){
int j = e[i];
d[j] --;
if(d[j] == 0) q.push(j);
}
}
return cnt >= n;
}
最短路
dijkstra
int dijkstra () {
memset (dis, 0x3f, sizeof dis);
dis[1] = 0;
//vis[1] = true;
for (int i = 1; i <= n; i ++) {
int t = -1;
for (int j = 1; j <= n; j ++) {
if (!vis[j] && (t == -1 || dis[j] < dis[t])) t = j;
}
for (int j = 1; j <= n; j ++) dis[j] = min (dis[j], a[t][j] + dis[t]);
vis[t] = true;
}
if (dis[n] == 0x3f3f3f3f) return -1;
return dis[n];
}
堆优化
int dijkstra(){
memset (dis, 0x3f, sizeof dis);
dis[1] = 0;
priority_queue<PII, vector<PII>, greater<PII>>heap;
heap.push({0, 1});//dis num
while (!heap.empty()){
auto t = heap.top();
heap.pop();
int num = t.second, distance = t.first;
if (vis[num]) continue;//pass
vis[num] = true;
for (int i = h[num]; i != -1; i = ne[i]){
int j = e[i];
if (dis[j] > distance + w[i]){
dis[j] = distance + w[i];
heap.push({dis[j], j});
}
}
}
if (dis[n] == 0x3f3f3f3f) return -1;
return dis[n];
}
Bellman_ford
void bellman_ford(){
memset (dis, 0x3f, sizeof dis);
dis[1] = 0;
for (int i = 0; i < k; i ++){ //最多经过k条边的最短距离
memcpy (backup, dis, sizeof dis);
for (int j = 1; j <= n; j ++)
for (int t = 1; t <= n; t ++){
dis[t] = min (dis[t], backup[j] + a[j][t]);
}
} //dis[n] > 0x3f3f3f3f/2 表示到不了
}
SPFA
int spfa () {
memset (dis, 0x3f, sizeof dis);
dis[1] = 0, vis[1] = true;
q.push (1);
while (!q.empty()) {
int t = q.front();
q.pop();
vis[t] = false;
for (int i = h[t]; i != -1; i = ne[i]) {
int j = e[i];
if (dis[j] > dis[t] + w[i]){
dis[j] = dis[t] + w[i];
if (!vis[j]) vis[j] = true, q.push (j);
}
}
}
return dis[n];
}
SPFA判负环
统计每个点的入队次数,如果某个点入队 n 次,则有负环
bool spfa () {
for (int i = 1; i <= n; i ++) {
q.push(i);
vis[i] = true;
}
while (!q.empty()) {
int t = q.front();
q.pop();
vis[t] = false;
for (int i = h[t]; i != -1; i = ne[i]) {
int j = e[i];
if (dis[j] > dis[t] + w[i]) {
dis[j] = dis[t] + w[i];
cnt[j] = cnt[t] + 1;
if (cnt[j] >= n) return true;
if (!vis[j]) q.push (j), vis[j] = true;
}
}
}
return false;
}
Floyd
void floyd () {
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= n; j ++){
if (i == j) a[i][j] = 0;
else a[i][j] = INF;
}
}
for (int k = 1; k <= n; k ++) {
for (int i = 1; i <= n; i ++) {
for (int j = 1; j <= n; j ++) {
a[i][j] = min (a[i][j], a[i][k] + a[k][j]);
}
}
}
}
最小生成树
prim
int prim(){
memset (dis, 0x3f, sizeof dis);
dis[1] = 0;
int ans = 0;
for (int i = 0; i < n; i ++){
int t = -1;
for (int j = 1; j <= n; j ++){
if (!vis[j] && (t == -1 || dis[t] > dis[j])) t = j;
}
if (dis[t] == INF) return INF;
ans += dis[t];
vis[t] = true;
for (int j = 1; j <= n; j ++) dis[j] = min (dis[j], a[t][j]);
}
return ans;
}
Kruscal
int kruskal(){
sort (e, e + m);
for (int i = 1; i <= n; i ++) fa[i] = i;
int ans = 0, cnt = 0;
for (int i = 0; i < m; i ++){
int a = e[i].a, b = e[i].b, w = e[i].w;
a = find(a), b = find(b);
if (a != b){
fa[a] = b;
ans += w;
cnt ++;
}//不连通就加入集合
}
if (cnt < n - 1) return INF;
return ans;
}
二分图
染色法判二分图
bool dfs(int u, int c){
color[u] = c;
for (int i = h[u]; i != -1; i = ne[i]){
int j = e[i];
if (color[j] == -1){
if (!dfs(j, !c)) return false;
}
else if (color[j] == c) return false;
}
return true;
}
bool check(){
memset (color, -1, sizeof color);
bool flag = true;
for (int i = 1; i <= n; i ++){
if (color[i] == -1 && !dfs(i, 0)) {
flag = false;
break;
}
}
return flag;
}
二分图最大匹配
bool find (int x){
for (int i = h[x]; i != -1; i = ne[i]){
int j = e[i];
if (!vis[j]){
vis[j] = true;
if (match[j] == 0 || find (match[j])){
match[j] = x;
return true;
}
}
}
return false;
}
/* //main函数中
for (int i = 1; i <= n1; i ++){
memset(vis, false, sizeof vis);
if (find(i))
ans ++;
}
*/
最近公共祖先LCA
倍增法
void bfs (int root) {
memset (depth, 0x3f, sizeof depth); //记得
queue <int> q;
q.push (root);
depth[root] = 1, depth[0] = 0;
while (!q.empty()) {
int t = q.front();
q.pop ();
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if (depth[j] > depth[t] + 1) {
depth[j] = depth[t] + 1;
q.push (j);
f[j][0] = t;
for (int k = 1; k <= 15; k++) {
f[j][k] = f[f[j][k-1]][k-1];
}
}
}
}
}
int lca (int a, int b) {
if (depth[a] < depth[b]) swap (a, b); //确保a往上跳
for (int k = 15; k >= 0; k--) {
if (depth[f[a][k]] >= depth[b]) { //注意是>=
a = f[a][k];
}
}
if (a == b) return a;
for (int k = 15; k >= 0; k--) {
if (f[a][k] != f[b][k]) {
a = f[a][k], b = f[b][k];
}
}
return f[a][0];
}
\(Tarjan\) 离线做法
void dfs (int v, int u) { //1, -1
for (int i = h[v]; ~i; i = ne[i]) {
int j = e[i];
if (j == u) continue;
d[j] = d[v] + w[i];
dfs (j, v);
}
}
int find (int x) {
if (x != fa[x])
fa[x] = find (fa[x]);
return fa[x];
}
void tarjan (int u) { //1
vis[u] = 1; //正在搜
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (vis[j]) continue;
tarjan(j);
fa[j] = u; //合并
}
for (auto i : q[u]) {
int v = i.first, id = i.second;
if (vis[v] == 2) {
int p = find(v);
ans[id] = d[u] + d[v] - 2*d[p];
}
}
vis[u] = 2; //搜过了
}
缩点
void tarjan (int u) {
dfn[u] = low[u] = ++ timestamp;
stk.push (u), in_stk[u] = true;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (!dfn[j]) {
tarjan(j);
low[u] = min (low[u], low[j]);
}
else if (in_stk[j]) low[u] = min (low[u], dfn[j]);
}
if (dfn[u] == low[u]) {
int y = 0;
scc_cnt ++;
do {
y = stk.top();
stk.pop();
in_stk[y] = false;
id[y] = scc_cnt, sz[scc_cnt] ++;
} while (y != u);
}
}
int main () {
memset (h, -1, sizeof h);
cin >> n >> m;
for (int i = 0; i < m; i ++) {
int a, b;
cin >> a >> b;
add (a, b);
}
for (int i = 1; i <= n; i++) {
if (!dfn[i]) tarjan(i);
}
for (int t = 1; t <= n; t++) {
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
int a = id[t], b = id[j];
if (a ^ b) dout[a] ++; //非拓扑图终点
}
}
int cnt = 0, ans = 0; //dout=0的点数
for (int i = 1; i <= scc_cnt; i++) {
if (dout[i]) continue;
cnt ++, ans += sz[i];
if (cnt > 1) { //出度为0的点数大于1,则这些点之间无法互相到达
ans = 0;
break;
}
}
cout << ans;
}
欧拉回路
void dfs (int u) {
for (int &i = h[u]; ~i; ) {
if (used[i]) {
i = ne[i];
continue;
}
used[i] = true;
if (ver == 1) used[i^1] = true; //无向图存边的规律
int t;
if (ver == 1) {
t = i / 2 + 1; //找节点
if (i & 1) t = -t;
}
else t = i + 1;
//记录答案
int j = e[i];
i = ne[i];
dfs (j);
ans[cnt ++] = t;
}
}
int main() {
memset(h, -1, sizeof h);
cin >> ver >> n >> m;
for (int i = 0; i < m; i++) {
int a, b;
cin >> a >> b;
add(a, b);
if (ver == 1) add(b, a); //无向边
din[b]++, dout[a]++;
}
if (ver == 1) {
for (int i = 1; i <= n; i++) {
if (din[i] + dout[i] & 1) {//无向图:每个点的度都为偶数
puts("NO");
return 0;
}
}
}
else {
for (int i = 1; i <= n; i++) {
if (din[i] != dout[i]) { //有向图:每个点的入度等于出度
puts("NO");
return 0;
}
}
}
for (int i = 1; i <= n; i++) {
if (~h[i]) {
dfs(i);
break;
}
}
if (cnt < m) { //不连通
puts("NO");
return 0;
}
puts("YES");
for (int i = cnt - 1; i >= 0; --i) cout << ans[i] << " ";
}
点分治
//长度不超过k的路有多少条
int getSize (int u, int fa) { //以u为根的子树大小
if (vis[u]) return 0;
int sz = 1;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (j == fa) continue;
sz += getSize (j, u);
}
return sz;
}
int getCenter (int u, int fa, int allSize, int &ct) { //求重心
if (vis[u]) return 0;
int sum = 1, maxSize = 1; //u的子树的结点个数
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (j == fa) continue;
int curSize = getCenter (j, u, allSize, ct);
sum += curSize;
maxSize = max (maxSize, curSize);
}
maxSize = max (maxSize, allSize - sum);
if (maxSize <= allSize / 2) ct = u; //不一定是重心,满足一半即可,保证logn
return sum;
}
void getDis (int u, int fa, int dis, int &len) { //当前子树上所有点到根的距离
if (vis[u]) return ;
dis_cur[len++] = dis;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (j == fa) continue;
getDis (j, u, dis + w[i], len); //dis + w[i]记得更新距离啊!!
}
}
int count (int *a, int sz) { //a[]中选两点满足dis<=m
sort (a, a + sz);
int cnt = 0; //满足条件的点对个数
for (int i = sz - 1, j = -1; i >= 0; i--) {
while (j < i - 1 && a[i] + a[j + 1] <= m) j++;
j = min (j, i - 1); //j左i右
cnt += j + 1;
}
return cnt;
}
int cal (int u) { //递归计算以u为根的答案
if (vis[u]) return 0;
getCenter (u, -1, getSize (u, -1), u);
vis[u] = true; //删重心
int len = 0, cnt = 0;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
int len_cur = 0; //当前子树大小
getDis (j, -1, w[i], len_cur);
cnt -= count (dis_cur, len_cur); //情况3: 容斥掉同一树内的点对
for (int k = 0; k < len_cur; k++) {
if (dis_cur[k] <= m) cnt++; //情况2: 某点是重心
dis_all[len++] = dis_cur[k];
}
}
cnt += count (dis_all, len); //情况3: 不同子树间的点对
for (int i = h[u]; ~i; i = ne[i]) cnt += cal (e[i]);
return cnt;
}
斯坦纳树
void dijkstra (int s) {
memset (vis, false, sizeof vis);
while (!q.empty ()) {
auto t = q.top ();
int ver = t.second;
q.pop ();
if (vis[ver]) continue;
vis[ver] = true;
for (int i = h[ver]; ~i; i = ne[i]) {
int j = e[i];
if (f[j][s] > f[ver][s] + w[i]) {
f[j][s] = f[ver][s] + w[i];
q.push ({f[ver][s], j});
}
}
}
}
int main () {
cin >> n >> m >> k;
memset (h, -1, sizeof h);
memset (f, 0x3f, sizeof f);
for (int i = 1; i <= m; i++) {
int a, b, c;
cin >> a >> b >> c;
add (a, b, c), add (b, a, c);
}
for (int i = 1; i <= k; i++) cin >> S[i], f[S[i]][1<<(i-1)] = 0;
for (int s = 1; s < (1 << k); s++) {
for (int i = 1; i <= n; i++) {
for (int t = s & (s - 1); t; t = s & (t - 1)) {
f[i][s] = min (f[i][s], f[i][t] + f[i][s ^ t]);
}
if (f[i][s] != inf) q.push ({f[i][s], i});
}
dijkstra (s);
}
cout << f[S[1]][(1<<k) - 1];
}
网络流
EK算法
bool bfs () {
queue<int> q;
memset (vis, false, sizeof vis);
memset (d, 0, sizeof d);
memset (pre, 0, sizeof pre);
q.push (S), vis[S] = true, d[S] = inf;
while (!q.empty()) {
int t = q.front();
q.pop();
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if (vis[j] || w[i] == 0) continue;
pre[j] = i;
vis[j] = true;
d[j] = min (d[t], w[i]);
if (j == T) return true;
q.push (j);
}
}
return false;
}
int EK () {
int ans = 0;
while (bfs ()) {
ans += d[T];
for (int i = T; i != S; i = e[pre[i]^1]) {
w[pre[i]] -= d[T], w[pre[i]^1] += d[T];
}
}
return ans;
}
Dinic
bool bfs () {
queue <int> q;
memset (d, -1, sizeof d);
q.push (S), d[S] = 0, cur[S] = h[S];
while (!q.empty ()) {
int t = q.front();
q.pop();
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if (d[j] != -1 || w[i] == 0) continue;
d[j] = d[t] + 1;
cur[j] = h[j];
if (j == T) return true;
q.push (j);
}
}
return false;
}
int find (int u, int limit) {
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i]) {
cur[u] = i; //当前弧优化
int j = e[i];
if (d[j] != d[u] + 1 || w[i] == 0) continue;
int t = find (j, min (w[i], limit - flow));
if (t == 0) d[j] = -1;
w[i] -= t, w[i^1] += t, flow += t;
}
return flow;
}
int dinic () {
int r = 0, flow;
while (bfs ()) while (flow = find (S, inf)) r += flow;
return r;
}
数据结构
ST表
void pre() {
Logn[1] = 0;
Logn[2] = 1;
for (int i = 3; i < maxn; i++) Logn[i] = Logn[i / 2] + 1;
}
int main() {
int n = read(), m = read();
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]));
}
return 0;
}
并查集
void init () {
for (int i = 1; i <= n; i ++) fa[i] = i, sz[i] = 1;
}
int find (int x) {
if (x != fa[x]) fa[x] = find (fa[x]);
return fa[x];
}
void Union (int x, int y) { //启发式合并
x = find (x), y = find (y);
if (x == y) return;
if (sz[x] > sz[y]) swap(xx, yy);
fa[x] = y;
sz[y] += sz[x]; //注意不要反了
}
带权并查集
int find (int x) {
if (x != fa[x]) {
int t = fa[x]; //记录原父节点编号
fa[x] = find(fa[x]); //父节点变为根节点,此时value[x]=父节点到根节点的权值
v[x] += v[t]; //当前节点的权值加上原本父节点的权值
}
return fa[x];
}
void Union (int x, int y) {
int xx = find (x), yy = find (y);
if (xx != yy) {
fa[xx] = yy;
v[xx] = v[y] - v[x] + s; //有时候可能会取模
}
}
树状数组
ll lowbit (ll x) {
return (x & (-x));
}
void add (int x, int d) {
while (x <= n) tr[x] += d, x += lowbit (x);
}
ll query (ll x) {
ll s = 0;
while (x > 0) s += tr[x], x -= lowbit (x);
return s;
}
线段树
单调修改
struct st{
int l, r;
int v;
}st[N * 4];
void build (int u, int l, int r){
st[u] = {l, r};
if (l == r) return;
int mid = l + r >> 1;
build (u << 1, l, mid);
build (u << 1 | 1, mid + 1, r);
}
int query(int u, int l, int r){
if (st[u].l >= l && st[u].r <= r) return st[u].v;
int mid = st[u].l + st[u].r >> 1;
int v = 0;
//注意注意!此处l r顺序不能变
if (l <= mid) v = query (u << 1, l, r);
if (r > mid) v = max (v, query (u << 1 | 1, l, r));
return v;
}
void modify (int u, int x, int v){
if (st[u].l == x && st[u].r == x){
st[u].v = v;
return;
}
int mid = st[u].l + st[u].r >> 1;
if (x <= mid) modify (u << 1, x, v);
else modify (u << 1 | 1, x, v);
st[u].v = max (st[u << 1].v, st[u << 1 | 1].v);
}
查询区间最大连续子段和
struct tree{
int l, r, sum;
int lmax, rmax, tmax;//最大前后缀和, 最大子段和
}st[4 * N];
void pushup (tree &u, tree &l, tree &r){
u.sum = l.sum + r.sum;
u.lmax = max (l.lmax, l.sum + r.lmax);//左半段中的最大 or 左半段和+右半段中的最大
u.rmax = max (r.rmax, r.sum + l.rmax);//右半段中的最大 or 右半段和+左半段中的最大
u.tmax = max (max (l.tmax, r.tmax), l.rmax + r.lmax);//左半段中的最大 or 右半段中的最大 or 左半段后缀和+右半段前缀和
}
void pushup (int u){
pushup (st[u], st[u << 1], st[u << 1 | 1]);
}
void build (int u, int l, int r){
if (l == r){
st[u] = {l, r, a[r], a[r], a[r], a[r]};
return;
}
st[u] ={l, r};
int mid = l + r >> 1;
build (u << 1, l, mid), build (u << 1 | 1, mid + 1, r);
pushup (u);
}
void modify (int u, int x, int v){
if (st[u].l == x && st[u].r == x) st[u] = {x, x, v, v, v, v};
else{
int mid = st[u].l + st[u].r >> 1;
if (x <= mid) modify (u << 1, x, v);
else modify (u << 1 | 1, x, v);
pushup (u);
}
}
tree query (int u, int l, int r){
if (st[u].l >= l && st[u].r <= r) return st[u];
int mid = st[u].l + st[u].r >> 1;
if (r <= mid) return query (u << 1, l, r);
else if (l > mid) return query (u << 1 | 1, l, r);
else{
auto le = query (u << 1, l, r);
auto ri = query (u << 1 | 1, l, r);
tree ans;
pushup (ans, le, ri);
return ans;
}
}
区间最大公约数
struct Node {
int l, r;
ll sum, d;
}st[N * 4];
ll gcd (ll a, ll b){
return b ? gcd (b, a % b) : a;
}
void pushup (Node &u, Node &l, Node &r){
u.sum = l.sum + r.sum;
u.d = gcd (l.d, r.d);
}//单点修改
void pushup (int u){
pushup (st[u], st[u << 1], st[u << 1 | 1]);
}//修改全部
void build (int u, int l, int r){
if (l == r){
ll t = w[r] - w[r - 1];
st[u] = {l, r, t, t};
return;
} //只修改叶子节点的值
st[u] = {l, r};
int mid = l + r >> 1;
build (u << 1, l, mid);
build (u << 1 | 1, mid + 1, r);
pushup (u);
}
void modify (int u, int x, ll v){ //这个地方v忘记写成ll了
if (st[u].l == x && st[u].r == x){
ll t = st[u].sum + v;
st[u] = {x, x, t, t};
}
else {
int mid = st[u].l + st[u].r >> 1;
if (x <= mid)
modify (u << 1, x, v);
else
modify (u << 1 | 1, x, v);
pushup (u);
}
}
Node query (int u, int l, int r){
if (st[u].l >= l && st[u].r <= r){
return st[u];
}
int mid = st[u].l + st[u].r >> 1;
if (r <= mid)
return query (u << 1, l, r);
else if (l > mid)
return query (u << 1 | 1, l, r);
else{
auto ll = query (u << 1, l, r), rr = query (u << 1 | 1, l, r);
Node res;
pushup (res, ll, rr);
return res;
}
}
int main (){
cin >> n >> m;
for (int i = 1; i <= n; i ++)
cin >> w[i];
build (1, 1, n);
while (m --){
ll l, r, d;
char op;
cin >> op;
if (op == 'C'){
cin >> l >> r >> d;
modify (1, l, d);
if (r + 1 <= n) //防越界
modify (1, r + 1, -d);
}
else {
cin >> l >> r;
auto le = query (1, 1, l);
Node ri ({0, 0, 0, 0});
if (l + 1 <= r) //防越界
ri = query (1, l + 1, r);
cout << abs (gcd (le.sum, ri.d)) << endl;//正余数
}
}
}
区间修改
struct Node {
int l, r;
ll sum, lazy;
}st[N * 4]; //千万别忘了 * 4
void pushup (int u){
st[u].sum = st[u << 1].sum + st[u << 1 | 1].sum;
}
void pushdown (int u){
auto &root = st[u], &st_l = st[u << 1], &st_r = st[u << 1 | 1];
if (root.lazy){
st_l.lazy += root.lazy, st_l.sum += (ll)(st_l.r - st_l.l + 1) * root.lazy;
st_r.lazy += root.lazy, st_r.sum += (ll)(st_r.r - st_r.l + 1) * root.lazy;
root.lazy = 0; //就是这里,更新的时候要写各自的区间
}
}
void build (int u, int l, int r){
if (l == r){
st[u] = {l, r, a[r], 0};
return;
}
st[u] = {l, r};
int mid = l + r >> 1;
build (u << 1, l, mid);
build (u << 1 | 1, mid + 1, r);
pushup (u);
}
void modify (int u, int l, int r, int d) {
if (st[u].l >= l && st[u].r <= r){
st[u].sum += (ll)(st[u].r - st[u].l + 1) * d;
st[u].lazy += d;
return;
}
pushdown (u); //懒标记传给子
int mid = st[u].l + st[u].r >> 1;
if (l <= mid)
modify (u << 1, l, r, d);
if (r > mid)
modify (u << 1 | 1, l, r, d);
pushup (u);
}
ll query (int u, int l, int r){
if (st[u].l >= l && st[u].r <= r){
return st[u].sum;
}
pushdown (u);
int mid = st[u].l + st[u].r >> 1;
ll res = 0;
if (l <= mid)
res += query (u << 1, l, r);
if (r > mid)
res += query (u << 1 | 1, l, r);
return res;
}
扫描线求面积并
struct Seg {
double x, y, yy;
int k;
bool operator < (const Seg &t) const {
return x < t.x;
}
}seg[N * 2];
struct Node {
int l, r;
int cnt;
double sum; //区间被覆盖的次数以及区间长度
}st[N * 8];
int find (double y) {
return lower_bound (v.begin(), v.end(), y) - v.begin();
} //找离散化后y的下标
void pushup (int u) {
if (st[u].cnt)
st[u].sum = v[st[u].r + 1] - v[st[u].l]; //区间更新
else {
if (st[u].l == st[u].r)
st[u].sum = 0; //一个点
else
st[u].sum = st[u << 1].sum + st[u << 1 | 1].sum; //熟悉的区间更新
}
}
void build (int u, int l, int r) {
if (l == r) {
st[u] = {l, r, 0, 0};
return ;
}
st[u] = {l, r, 0, 0};
int mid = l + r >> 1;
build (u << 1, l, mid);
build (u << 1 | 1, mid + 1, r);
}
void modify (int u, int l, int r, int k) {
if (st[u].l >= l && st[u].r <= r) {
st[u].cnt += k;
pushup (u);
return ;
}
int mid = st[u].l + st[u].r >> 1;
if (l <= mid)
modify (u << 1, l, r, k);
if (r > mid)
modify (u << 1 | 1, l, r, k);
pushup (u);
}
int main () {
int n, T = 1;
while (cin >> n, n) {
v.clear();
for (int i = 0, j = 0; i < n; i ++) { //后面有用到n的话最好不要轻易n--
double a, b, c, d;
cin >> a >> c >> b >> d; //挖草!!是输入出了问题。。。x1 y1 x2 y2的顺序
seg[j ++] = {a, c, d, 1}; //左纵边
seg[j ++] = {b, c, d, -1}; //右纵边
v.push_back (c), v.push_back (d);
}
sort (v.begin(), v.end());
v.erase (unique (v.begin(), v.end()), v.end()); //离散化
build (1, 0, v.size() - 2); // n个节点对应(n-1)个区间,下标从0开始,故为(n-2)
sort (seg, seg + n * 2); // 纵边按x升序排序
double ans = 0;
for (int i = 0; i < n * 2; i ++) {
if (i > 0) // 从第2条纵边开始
ans += st[1].sum * (seg[i].x - seg[i - 1].x); //小矩形
modify (1, find (seg[i].y), find(seg[i].yy) - 1, seg[i].k);
}
printf("Test case #%d\n", T ++ );
printf("Total explored area: %.2lf\n\n", ans);
}
}
可持久化线段树
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5, M = 1e4 + 5;
int n, q, a[N], root[N], idx;
vector<int> v; //离散化
int find (int x) { //离散化后查找下标
return lower_bound (v.begin (), v.end (), x) - v.begin ();
}
struct Node {
int l, r, cnt;
}st[N * 4 + N * 17]; //4N + NlogN
int build (int l, int r) {
int p = ++idx;
if (l == r) return p;
int mid = l + r >> 1;
st[p].l = build (l, mid), st[p].r = build (mid + 1, r);
return p;
}
int insert (int u, int l, int r, int x) {
int p = ++idx;
st[p] = st[u];
if (l == r) {
st[p].cnt++;
return p;
}
int mid = l + r >> 1;
if (x <= mid) st[p].l = insert (st[u].l, l, mid, x);
else st[p].r = insert (st[u].r, mid + 1, r, x);
st[p].cnt = st[st[p].l].cnt + st[st[p].r].cnt;
return p;
}
int query (int q, int p, int l, int r, int k) {
if (l == r) return r;
int mid = l + r >> 1;
int cnt = st[st[q].l].cnt - st[st[p].l].cnt;
if (k <= cnt) return query (st[q].l, st[p].l, l, mid, k);
return query (st[q].r, st[p].r, mid + 1, r, k - cnt);
}
int main () {
cin >> n >> q;
for (int i = 1; i <= n; i++) {
cin >> a[i];
v.push_back (a[i]);
}
sort (v.begin (), v.end ());
v.erase (unique (v.begin (), v.end ()), v.end ());
int m = v.size ();
root[0] = build (0, m - 1);
for (int i = 1; i <= n; i++) root[i] = insert (root[i-1], 0, m - 1, find (a[i]));
while (q--) {
int l, r, k;
cin >> l >> r >> k;
cout << v[query (root[r], root[l-1], 0, m - 1, k)] << endl;
}
//cout << log2(N);
}
珂朵莉树
有区间赋值操作且数据随机的题目
(玄学暴力)
struct node {
ll l, r;
mutable ll v;
node(ll l, ll r, ll v) : l(l), r(r), v(v) {}
bool operator<(const node &o) const { return l < o.l; }
};
set<node> tree;
auto split(ll pos) {
auto it = tree.lower_bound(node(pos, 0, 0));
if (it != tree.end() && it->l == pos)
return it;
it--;
ll l = it->l, r = it->r, v = it->v;
tree.erase(it);
tree.insert(node(l, pos - 1, v));
return tree.insert(node(pos, r, v)).first;
}
void assign(ll l, ll r, ll v) {
auto end = split(r + 1), begin = split(l);
tree.erase(begin, end);
tree.insert(node(l, r, v));
}
其它
sqrt微调
int sqrtx (int x) {
int k = sqrt (x);
while (k * k >= x) k--;
while ((k + 1) * (k + 1) < x) k++;
return k;
}