文章目录
动态规划
多重背包二进制优化
/************************
User:Mandy.H.Y
Language:c++
Problem:luogu
Algorithm:
Date:2019.8.30
************************/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 205;
const int maxk = 2e4 + 5;
int b[maxn],c[maxn],new_b[4005],new_c[4005],new_id[4005];
int dp[maxk],ans[maxn];
int n,k,cnt;
bool vis[3205][maxk];
void readdata(){
read(n);
for(int i = 1;i <= n; ++ i) read(b[i]);
for(int i = 1;i <= n; ++ i) read(c[i]);
read(k);
}
void work(){
for(int i = 1;i <= n; ++ i){
c[i] = min(c[i],k/b[i]);
for(int j = 1;j <= c[i]; j <<= 1){
new_b[++cnt] = b[i] * j;
new_c[cnt] = j;
new_id[cnt] = i;
c[i] -= j;
}
if(c[i]){
new_b[++cnt] = b[i] * c[i];
new_c[cnt] = c[i];
new_id[cnt] = i;
c[i] = 0;
}
}
memset(dp,0x3f3f3f3f,sizeof(dp));
dp[0] = 0;
for(int i = 1;i <= cnt; ++ i){
for(int j = k;j >= new_b[i]; -- j){
if(dp[j] > dp[j - new_b[i]] + new_c[i]){
dp[j] = dp[j - new_b[i]] + new_c[i];
vis[i][j] = 1;
}
}
}
put(dp[k]);
putchar('\n');
int i = cnt,j = k;
while(i >= 1){
if(vis[i][j])
j = j - new_b[i],ans[new_id[i]] += new_c[i];
--i;
}
for(int i = 1;i <= n; ++ i){
put(ans[i]);
putchar(' ');
}
// printf("\n%lf",(double)sizeof(vis)/1024/1024);
}
int main(){
// file();
readdata();
work();
return 0;
}
动规矩阵优化
GTExam
/**********************
User:Mandy
Language:c++
Problem:luogu 3193 GT Exam
**********************/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxm = 25;
ll n,m,mod;
char a[maxm];
int fail[maxm];
int g[maxm][maxm];
int f[maxm];
void readdata(){
read(n);read(m);read(mod);
scanf("%s",a+1);
}
void multi(){
int c[maxm];
memset(c,0,sizeof(c));
for(int i = 0;i < m; ++ i)
for(int j = 0;j < m; ++ j)
c[j] = (c[j] + f[i] * g[i][j] % mod) % mod;
memcpy(f,c,sizeof(c));
}
void mulself(){
int c[maxm][maxm];
memset(c,0,sizeof(c));
for(int i = 0;i < m; ++ i)
for(int k = 0;k < m; ++ k)
for(int j = 0;j < m; ++ j)//不能算匹配成功的
c[i][j] = (c[i][j] + g[i][k] * g[k][j] % mod) % mod;
memcpy(g,c,sizeof(c));
}
void get_g(){//求出在i位置后面加一个字符使匹配长度变为j的方案数g[i][j]
int j = 0;
for(int i = 1;i < m; ++ i){
while(j > 0 && a[j+1] != a[i+1]) j = fail[j];
if(a[j+1] == a[i+1]) j++;
fail[i+1] = j;
}//KMP fail
j = 0;
for(int i = 0;i < m; ++ i){
for(int k = 0;k <= 9; ++ k){
j = i;
while(j && a[j+1]!=(k^48)) j = fail[j];
if(a[j+1] == (k^48)) j++;
g[i][j] = (g[i][j] + 1) % mod;
//0也是要计入的
}
}
}
void work(){
get_g();
f[0] = 1;
while(n) {
if(n&1) multi();
n >>= 1 ;
mulself();
}
int ans = 0;
for(int i = 0;i < m; ++ i) ans = (ans + f[i]) % mod;
put(ans%mod);
}
int main(){
// file();
readdata();
work();
return 0;
}
四边形不等式
/***************************
User:Mandy
Language:c++
Problem:stone
***************************/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2005;
int sum[maxn];
int fa[maxn][maxn],fi[maxn][maxn];
int s[maxn][maxn];
int n;
void readdata(){
read(n);
for(int i = 1;i <= n; ++ i){
read(sum[i]);
sum[i + n] = sum[i];
}
n <<= 1;n--;
for(int i = 1;i <= n; ++ i){
sum[i] += sum[i - 1];
s[i][i] = i;
}
}
void work(){
for(int i = n;i >= 1; -- i)
for(int j = i+1;j <= n; ++ j){
int tmp = 0x3f3f3f3f;
int id = 0;
fa[i][j] = max(fa[i][j-1],fa[i+1][j]) + sum[j] - sum[i-1];
for(int k = s[i][j-1];k <= s[i+1][j];++ k){
int cur = fi[i][k] + fi[k+1][j] + sum[j] - sum[i-1];
if(cur < tmp){
tmp = cur;
id = k;
}
}
fi[i][j] = tmp;
s[i][j] = id;
}
int ansmax = 0,ansmin = 0x3f3f3f3f;
n=(n+1)>>1;
for(int i = 1;i <= n; ++ i){
ansmax = max(ansmax,fa[i][i+n-1]);
ansmin = min(ansmin,fi[i][i+n-1]);
}
printf("%d\n%d",ansmin,ansmax);
}
int main(){
readdata();
work();
return 0;
}
斜率优化
/*************************
User:Mandy.H.Y
Language:c++
Problem:luogu3195 Toy
Algorithm:斜率优化
Date:2019.7.24
Scores:
*************************/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e4 + 5;
int n,L;
long long a[maxn],b[maxn],dp[maxn];
long long l,r,q[maxn << 1];
void readdata(){
read(n);read(L);
++L;
for(int i = 1;i <= n; ++ i){
long long x;read(x);
a[i] = a[i - 1] + x;
}
for(int i = 1;i <= n; ++ i){
a[i] += i;
b[i] = a[i] + L;
}//i不能累加,要单独加上去
}
void work(){
l = r = 1;//保证队列中至少两个元素
b[0] = L;//初始化
for(int i = 1;i <= n; ++ i){
long long k = a[i] << 1;
while(l < r &&
(b[q[l + 1]] * b[q[l + 1]] + dp[q[l + 1]] - b[q[l]] * b[q[l]] - dp[q[l]]) <=
(k * (b[q[l + 1]] - b[q[l]])))
++ l;
dp[i] = dp[q[l]] + (a[i] - b[q[l]]) * (a[i] - b[q[l]]);
while(l < r &&
(dp[q[r]] + b[q[r]] * b[q[r]] - dp[q[r - 1]] - b[q[r - 1]] * b[q[r - 1]]) * (b[i] - b[q[r]]) >=
(dp[i] + b[i] * b[i] - dp[q[r]] - b[q[r]] * b[q[r]]) * (b[q[r]] - b[q[r - 1]]))
-- r;
q[++ r] = i;
}
put(dp[n]);
}
int main(){
// file();
readdata();
work();
return 0;
}
树的最长链
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
int n,m,size,first[maxn],cnt,mdis;
int father[maxn],ans[maxn];
int tmp[maxn][2];
struct Edge{
int v,nt;
}edge[maxn << 1];
template<class T>inline void read(T &x){
x = 0;bool flag = 0;char ch = getchar();
while(!isdigit(ch)) flag |= ch == '-',ch = getchar();
while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();
if(flag) x = -x;
}
void eadd(int u,int v){
edge[++size].v = v;
edge[size].nt = first[u];
first[u] = size;
}
void readdata(){
read(n);
for(int i = 1;i < n; ++ i){
int u,v;read(u);read(v);
eadd(u+1,v+1);eadd(v+1,u+1);
}
}
void dfs(int u,int fa){
for(int i = first[u];i;i = edge[i].nt){
int v = edge[i].v;
if(v == fa) continue;
dfs(v,u);
if(tmp[v][0]+1>=tmp[u][0]) tmp[u][1] = tmp[u][0],tmp[u][0] = tmp[v][0]+1;
else if(tmp[v][0]+1>tmp[u][1]) tmp[u][1] = tmp[v][0]+1;
}
mdis = max(tmp[u][0]+tmp[u][1],mdis);
}
void dfs1(int u,int fa,int dis){
for(int i = first[u];i;i = edge[i].nt){
int v = edge[i].v;
if(v == fa) continue;
if(tmp[v][0]+1==tmp[u][0]) dfs1(v,u,max(dis,tmp[u][1])+1);//注意+1的位置
else dfs1(v,u,max(dis,tmp[u][0])+1);
}
int cur = max(dis,tmp[u][1])+tmp[u][0];
if(cur == mdis) ans[++cnt] = u;
}
void work(){
dfs(1,0);
dfs1(1,0,0);
sort(ans+1,ans+1+cnt);
for(int i = 1;i <= cnt; ++ i) put(ans[i]-1),putchar('\n');
}
int main(){
// file();
readdata();
work();
return 0;
}
基环树Dp
骑士
/*********************
User:Mandy.H.Y
Language:c++
Problem:luogu2607
Algorithm:
*********************/
//嗯,谁说多了一条边就只有一个环?
//可能多个连通块,多个环
//这道题连单向边
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 5;
int n,size,first[maxn];
int r1,r2;
long long val[maxn];
long long dp[maxn][4];//long long
bool vis[maxn],used[maxn];
struct Edge{
int v,nt;
}edge[maxn<<1];
void eadd(int u,int v){
edge[++size].v = v;
edge[size].nt = first[u];
first[u] = size;
}
void readdata(){
read(n);
for(int i = 1;i <= n; ++ i){
read(val[i]);
int u;read(u);
eadd(u,i);
//被讨厌的指向讨厌的
//单向边就好
}
}
void find_circle(int u){
vis[u] = 1;//标记是否有环
for(int i = first[u];i;i = edge[i].nt){
int v = edge[i].v;
if(used[v]) continue;
//走下去没环的
if(vis[v]){
r1 = u;
r2 = v;
continue;
}
find_circle(v);
}
used[u] = 1;//标记连通块
//如果放前面就直接跳走了
vis[u] = 0;//撤标记
//不然可能判错
}
void dfs(int u,int rt){
used[u] = 1;
dp[u][0] = 0;
dp[u][1] = val[u];
for(int i = first[u];i;i = edge[i].nt){
int v = edge[i].v;
if(v == rt) continue;//重新走到根
dfs(v,rt);
dp[u][0] += max(dp[v][0],dp[v][1]);
dp[u][1] += dp[v][0];
}
}
void work(){
long long ans = 0;
for(int i = 1;i <= n; ++ i){
if(!used[i]){
r1 = r2 = 0;
long long cur = -100;
find_circle(i);
if((!r1) && (!r2)) continue;
//没走到环的
//因为是有向边
dfs(r1,r1);
cur = max(cur,dp[r1][0]);
dfs(r2,r2);//因为r1与r2不共存,所以分别为根
cur = max(cur,dp[r2][0]);
ans += cur;
}
}
put(ans);
}
int main(){
// file();
readdata();
work();
return 0;
}
基环树直径
/**********************
User:Mandy.H.Y
Language:c++
Problem:
Algorithm:
**********************/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 5;
int n,first[maxn],size = 1,s[maxn << 1],tp,q[maxn],l,r;
struct Edge{
int v,nt;
long long w;
}edge[maxn << 1];
long long tmp[maxn][2],ans,cur,dis[maxn << 1],judge;
bool dfn[maxn],vis[maxn];
void eadd(int u,int v,int w){
edge[ ++ size].v = v;
edge[size].w = w;
edge[size].nt = first[u];
first[u] = size;
}
void readdata(){
read(n);
for(int i = 1;i <= n; ++ i){
int v,w;read(v);read(w);
eadd(i,v,w);eadd(v,i,w);
}
}
bool get_circle(int u,int e){//用反向边判断,因为有可能有一条边连向父亲
if(dfn[u]) return 0;
if(vis[u]) {dfn[u] = 1;dis[tp] = 0;s[tp++] = u;return 1;}//环开始
vis[u] = 1;
for(int i = first[u];i;i = edge[i].nt){
int v = edge[i].v;if(i == (e ^ 1)) continue;
if(get_circle(v,i)) {
if(u == s[0]) {dis[tp] = dis[tp - 1] + edge[i].w;judge = 1;return 1;}//环结束
if(!judge){dfn[u] = 1;dis[tp] = dis[tp - 1] + edge[i].w;s[tp++]=u;}
return 1;
}
}
return 0;
}
void get_chain(int u,int e){
for(int i = first[u];i;i = edge[i].nt){
int v = edge[i].v;if(i == (e ^ 1) || dfn[v]) continue;//不能又走到环上
get_chain(v,i);
if(tmp[v][0] + edge[i].w >= tmp[u][0]){
tmp[u][1] = tmp[u][0];tmp[u][0] = tmp[v][0] + edge[i].w;
} else tmp[u][1] = max(tmp[u][1],tmp[v][0] + edge[i].w);
}
cur = max(cur,tmp[u][0] + tmp[u][1]);
dfn[u] = 1;
}
long long get(int x){
tp = 0;cur = 0;judge = 0;
l = r = 0;
get_circle(x,0);//找环
for(int i = 0;i < tp; ++ i) get_chain(s[i],0),s[tp + i] = s[i];
for(int i = tp + 1;i < (tp << 1); ++ i) dis[i] = dis[i - 1] + dis[i - tp] - dis[i - tp - 1];
for(int i = 0;i < (tp << 1); ++ i){//单队
while(l < r && i - q[l] >= tp) l ++;
if(l < r) cur = max(cur,dis[i] - dis[q[l]] + tmp[s[i]][0] + tmp[s[q[l]]][0]);
while(l < r && tmp[s[i]][0]-tmp[s[q[r-1]]][0] >= dis[i] - dis[q[r-1]]) r -- ;
q[r ++ ] = i;
}
return cur;
}
void work(){
for(int i = 1;i <= n; ++ i){
if(!dfn[i]) {
ans += get(i);
}
}
put(ans);
}
int main(){
readdata();
work();
return 0;
}
LCS
/************************
User:Mandy.H.Y
Language:c++
Problem:luogu1439 LCS
Algorithm:
Date:2019.7.29
Scores:
************************/
//他日若云月抛砚执笔,为我题扇面
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
int n;
int a[maxn],b[maxn],t[maxn];
template<class T>inline void read(T &x){
x = 0;bool flag = 0;char ch = getchar();
while( ! isdigit(ch)) flag |= ch == '-',ch = getchar();
while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();
if(flag) x = -x;
}
template<class T>void putch(const T x){
if(x > 9) putch(x / 10);
putchar(x % 10 | 48);
}
template<class T>void put(const T x){
if(x < 0) putchar('-'),putch(-x);
else putch(x);
}
void readdata(){
read(n);
for(int i = 1;i <= n; ++ i) read(a[i]);
for(int i = 1;i <= n; ++ i) read(b[i]);
}
int erfen(int l,int r,int x){
while(l <= r){
int mid = (l + r) >> 1;
if(t[mid] <= x) l = mid + 1;
else r = mid - 1;
}
return l;
}
void work(){
for(int i = n;i >= 1; -- i) t[a[i]] = i;
for(int i = 1;i <= n; ++ i) a[i] = t[b[i]];
memset(t,0,sizeof(t));
int len = 0;t[0] = -1;
for(int i = 1;i <= n; ++ i){
if(t[len] < a[i]) t[++len] = a[i];
else{
int pos = erfen(1,len,a[i]);
t[pos] = a[i];
}
}
put(len);
}
int main(){
// file();
readdata();
work();
return 0;
}
最大字段和
/**********************
User:Mandy.H.Y
Language:c++
Problem:hdu max sum
Algorithm:
Scores:
**********************/
//hdu与poj都不能用万能头文件
//#include<bits/stdc++.h>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn = 1e5 + 5;
int t,n,ans,ansl,ansr;
int a[maxn];
int l[maxn],r[maxn],sum[maxn];
void readdata(){
read(n);
for(int i = 1;i <= n; ++ i) read(a[i]);
}
void work(int j){
ans = -2e9;
sum[0] = -1;//初始化
for(int i = 1;i <= n;++ i){
l[i] = i;r[i] = i;
if(sum[i - 1] >= 0){//要输出第一个位置,需要sum[0]初始化为负
sum[i] = sum[i - 1] + a[i];
l[i] = l[i - 1];
}else sum[i] = a[i];
if(ans < sum[i]){//要输出第一个位置
ans = sum[i];
ansl = l[i];
ansr = r[i];
}
}
printf("Case %d:\n",j);
put(ans);putchar(' ');
put(ansl);putchar(' ');
put(ansr);
puts("");//一个空格和换行符引发的血案
if(j != t) puts("");//要空行
}
int main(){
read(t);
for(int i = 1;i <= t; ++ i){
readdata();
work(i);
}
return 0;
}
最大子矩阵和
/************************
User:Mandy.H.Y
Language:c++
Problem:hdu
Algorithm:
Date:2019.8.16
************************/
#include<cstdio>
#include<iomanip>
#include<cmath>
#include<algorithm>
#define Max(x,y) ((x)>(y)?(x):(y))
using namespace std;
const int maxn = 105;
int n;
int t[maxn];
int sum[maxn][maxn];
void readdata(){
for(int i = 1;i <= n; ++ i)
for(int j = 1;j <= n; ++ j){
read(sum[i][j]);
sum[i][j] += sum[i - 1][j];
}
}
void work(){
int ans = -2e9;
for(int i = 1;i <= n; ++ i)
for(int j = i;j <= n; ++ j){
for(int k = 1;k <= n; ++ k){
t[k] = sum[j][k] - sum[i - 1][k];
if(t[k - 1] > 0) t[k] += t[k - 1];
ans = Max(ans,t[k]);
}
}
put(ans);
puts("");
}
int main(){
// file();
while(~scanf("%d",&n)){
readdata();
work();
}
return 0;
}
最大子矩阵问题
枚举所有极大子矩形
/*************************
User:Mandy.H.Y
Language:c++
Problem:luogu1578
Algorithm:
Date: 2019.8.14
*************************/
#include<bits/stdc++.h>
#define Max(x,y) ((x) > (y) ? (x) : (y))
#define Min(x,y) ((x) < (y) ? (x) : (y))
using namespace std;
const int maxn = 3e4 + 5;
const int maxp = 5e3 + 10;
typedef long long ll;
int n,m,p;
long long ans = 0;
struct Node{
int x,y;
}node[maxp];
void readdata(){
read(n);read(m);read(p);
for(int i = 1;i <= p;++ i) read(node[i].x),read(node[i].y);
node[++p].x = 0;node[p].y = 0;
node[++p].x = 0;node[p].y = m;
node[++p].x = n;node[p].y = 0;
node[++p].x = n;node[p].y = m;//防止超出矩形边界
}
bool cmp1(const Node &a,const Node &b){
return a.y < b.y;
}
bool cmp2(const Node &a,const Node &b){
return a.x < b.x;
}
void work(){
sort(node + 1,node + p + 1,cmp1);
for(int i = 1;i <= p; ++ i){
int up = 0,down = n,l = m - node[i].y;
for(int j = i + 1;j <= p; ++ j){//右边在矩形边界与右边是障碍物的情况
//down - up不是up - down
if(node[j].x <= up || node[j].x >= down) continue;//在上下界之外不考虑
if((down - up) * l <= ans) break;//剪枝
ans = max(ans,(down - up) * ((long long)node[j].y - node[i].y));//作为右边界
if(node[j].x == node[i].x) break;//如果横坐标相等,后来就都是0
if(node[j].x < node[i].x) up = node[j].x;
else down = node[j].x;
}
up = 0,down = n,l = node[i].y;
for(int j = i - 1;j >= 1; -- j){//左边在矩形边界上
if(node[j].x <= up || node[j].x >= down) continue;
if((down - up) * l <= ans) break;
ans = max(ans,(down - up) * ((long long)node[i].y - node[j].y));
if(node[j].x == node[i].x) break;
if(node[j].x < node[i].x) up = node[j].x;
else down = node[j].x;
}
}
sort(node + 1,node + p + 1,cmp2);
for(int i = 2;i <= p; ++ i) ans = max(ans,(long long)m * (node[i].x - node[i - 1].x));
//考虑左右都在矩形边界上的情况
put(ans);
}
int main(){
// file();
readdata();
work();
return 0;
}
悬线法
/**********************
User:Mandy.H.Y
Language:
Problem:4147
Algorithm:
**********************/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1005;
int n,m,ans = 0;
char a[maxn][maxn];
int _left[maxn][maxn];
int _right[maxn][maxn];
int _over[maxn][maxn];
void readdata(){
read(n);read(m);
for(int i = 1;i <= n; ++ i){
for(int j = 1;j <= m; ++ j){
char c = getchar();
while(c != 'R' && c != 'F') c = getchar();
a[i][j] = c;
if(c == 'R') continue;//题目要求必须等于'F'
_left[i][j] = _right[i][j] = j;
_over[i][j] = 1;
}
}
}
void work(){
for(int i = 1;i <= n; ++ i)
for(int j = 2;j <= m; ++ j)
if(a[i][j] == a[i][j - 1] && a[i][j] == 'F')
_left[i][j] = _left[i][j - 1];
for(int i = 1;i <= n; ++ i)
for(int j = m - 1;j >= 1; -- j)
if(a[i][j] == a[i][j + 1] && a[i][j] == 'F')//注意倒序 + 1
_right[i][j] = _right[i][j + 1];
for(int i = 1;i <= n; ++ i){
for(int j = 1;j <= m; ++ j){
if(i > 1 && a[i][j] == a[i - 1][j] && a[i][j] == 'F'){//注意边界
_left[i][j] = max(_left[i][j],_left[i - 1][j]);
_right[i][j] = min(_right[i][j],_right[i - 1][j]);
_over[i][j] = _over[i - 1][j] + 1;
}
int len1 = _right[i][j] - _left[i][j] + 1;
ans = max(ans,_over[i][j] * len1);
}
}
put(ans * 3);
}
int main(){
// file();
readdata();
work();
return 0;
}
数位DP
/************************
User:Mandy.H.Y
Language:c++
Problem:luogu2602
Algorithm:数位 DP DFS
************************/
#include<bits/stdc++.h>
//这道题如果从 0 开始的话还要加个特判
using namespace std;
const int maxn=35;
typedef long long LL;
LL a,b;
LL num[15],dp[20][20];
void file(){
freopen("count.in","r",stdin);
}
LL dfs(LL dig,int limit,int lead,LL sum,LL pos){
//pos 位数
//dig 要找的数字
//limit 这一位是否有限制
//lead 是否有前导0 如果lead==0 那么有前导0
//sum : 搜到pos位时出现了多少个dig
if(!pos) return sum;
if(!limit && lead && dp[pos][sum]!=-1) return dp[pos][sum];
//没上限且没有前导0
//主要防止0算错 eg.0000 0010
LL ans=0;
int top=limit ? num[pos] : 9;
for(int i=0;i<=top;++i)
ans+=dfs(dig,limit&&i==top,i||lead,sum+((lead||i)&&(i==dig)),pos-1);
if(!limit && lead) dp[pos][sum]=ans;
return ans;
}
LL work(LL dig,LL r){
memset(num,0,sizeof(num));
memset(dp,-1,sizeof(dp));
LL x=r;
if(!x) num[0]=1;
while(x){
++num[0];
num[num[0]]=x%10;
x/=10;
}
return dfs(dig,1,0,0,num[0]);
}
int main(){
// file();
read(a);read(b);
for(LL i=0;i<=9;++i){
put(work(i,b)-work(i,a-1));
putchar(' ');
}
return 0;
}
二次扫描与换根
意会意会
数学
乘法逆元
exgcd
#include<bits/stdc++.h>
using namespace std;
void exgcd(int a,int b,int &k,int &p){
if(!b) k = 1,p = 0;
else exgcd(b,a % b,p,k),p -= a/b*k;
}
int main(){
int a,b;
scanf("%d%d",&a,&b);
int k,p;
exgcd(a,b,k,p);
if(k <= 0) k += ((-k)/b+1)*b;
printf("%d",k);
return 0;
}
ksm
#include<bits/stdc++.h>
using namespace std;
const int mod = 19260817;
long long a,b;
long long qk_p(long long b,long long p){
long long ans = 1;
while(p){
if(p&1) ans = ans*b%mod;
p>>=1;
b = b * b%mod ;
}
return ans;
}
long long inv(long long x){
return qk_p(x,mod-2);
}
int main(){
read(a);read(b);
if(!b){
puts("Angry!");
return 0;
}
printf("%lld",a*inv(b)%mod);
return 0;
}
阶乘逆元
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2005;
const int mod = 10007;
long long n,m,k,a,b;
long long fact[maxn],inv[maxn];
void readdata(){
read(a);read(b);read(k);read(n);read(m);
}
long long ksm(long long x,long long p){
long long ans = 1;
while(p){
if(p & 1) ans = ans * x % mod;
p >>= 1;
x = x * x % mod;
}
return ans;
}
long long Inv(long long x){
return ksm(x,mod-2);
}
void work(){
fact[1] = 1;
for(int i = 2;i <= k; ++ i) fact[i] = fact[i - 1] * i % mod;
inv[k] = Inv(fact[k]);
for(int i = k - 1;i >= 1; -- i) inv[i] = inv[i + 1] * (i + 1) % mod;//阶乘逆元
long long ansa = ksm(a,n);
long long ansb = ksm(b,m);
put(fact[k]*inv[n]%mod*inv[k-n]%mod*ansa%mod*ansb%mod);
}
int main(){
readdata();
work();
return 0;
}
线性求逆元
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e6 + 5;
int n,p;
int inv[maxn];
int main(){
scanf("%d%d",&n,&p);
inv[1] = 1;
for(int i = 2;i <= n; ++ i){
inv[i] = (p-p/i)*(long long)inv[p%i]%p;
}
for(int i =1;i <= n; ++ i){
put(inv[i]);
putchar('\n');
}
return 0;
}
//给定n个数,求逆元
//思路1:把乘逆元转化为除,通分,前后缀积
//思路2:用类似阶乘逆元的方法,将这n个数相乘
//没说p一定是质数,用exgcd比较好
//不要把函数类型名写错了
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e6 + 5;
const long long p = 1000000007ll;
long long n;
long long k = 998244353ll;
long long a[maxn],s[maxn],s1=1ll,ans = 0ll,sk = 1ll;
char *TT,*mo,but[(1 << 18) + 2];
#define getchar() ((TT == mo && (mo = ((TT = but) + fread(but,1,1<<18,stdin)),TT == mo)) ? -1 : *TT++)
template<class T>inline void read(T &x){
x = 0;char ch = getchar();bool flag = 0;
while(!isdigit(ch)) flag |= ch == '-',ch = getchar();
while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();
if(flag) x = -x;
}
void exgcd(long long x,long long P,long long &c,long long &d){
if(!P) c = 1,d = 0;
else exgcd(P,x%P,d,c),d-=x/P*c;
}
/*
void exgcd(long long a,long long b)
{
if(a==1&&b==0)
{
c=1;d=0;
return;
}
exgcd(b,a%b);
long long x1=c;
c=d;
d=x1-(a/b)*c;
return;
}*/
long long qk_p(long long x,long long P){
long long base = 1;
while(P){
if(P&1) base = base*x%p;
P>>=1;
x = x*x%p;
}
return base;
}
int main(){
// freopen("1.in","r",stdin);
read(n);
s[0]=1ll;
for(int i = 1;i <= n; ++ i) read(a[i]),s[i] = s[i-1]*a[i]%p;
long long c=0ll,d=0ll;
exgcd(s[n],p,c,d);
c = (c % p+p)%p;
// long long c = qk_p(s[n],p-2);
for(int i = n;i >= 1; -- i){
ans = (ans + c*s[i-1]%p*sk)%p;
//卡常少一个mod
c=c*a[i]%p;
sk = sk*k%p;
}
printf("%lld",ans);
return 0;
}
/*
int main(){
// freopen("in.txt","r",stdin);
read(n);
s[0]=1;
for(int i = 1;i <= n; ++ i) read(a[i]),s[i] = s[i-1]*a[i]%p;
for(int i = n;i >= 1; -- i){
ans = (ans + s[i-1]*s1%p*sk)%p;
s1 = s1*a[i]%p;
sk = sk*k%p;
}
long long c,d;
exgcd(s[n],p,c,d);
if(c <= 0) c+=((-c)/p+1)*p;
c%=p;
printf("%lld",ans*c%p);
return 0;
}
*/
组合数
luogu3807
Lucas
#define int long long
int t,n,m,p,f[100005];
int pow(int x,int y,int p){ //快速幂
x%=p;
int ans=1;
for(int i=y;i;i>>=1,x=x*x%p) if(i&1) ans=ans*x%p;
return ans;
}
int C(int n,int m,int p){ //求组合数
if(m>n) return 0;
return ((f[n]*pow(f[m],p-2,p))%p*pow(f[n-m],p-2,p)%p);
}
int lucas(int n,int m,int p){ //Lucas定理
if(!m) return 1;
return C(n%p,m%p,p)*lucas(n/p,m/p,p)%p;
}
signed main(){
scanf("%d",&t);
while(t--){
scanf("%d%d%d",&n,&m,&p);
f[0]=1;
for(int i=1;i<=p;i++)
f[i]=(f[i-1]*i)%p;
printf("%lld\n",lucas(n+m,m,p));
}
}
欧拉定理
a^b mod p
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
typedef long long ll;
long long a,m,b,pm;
template<class T>inline void get(T &x){
x = 0;bool flag = 0;char ch = getchar();
while( ! isdigit(ch)) ch = getchar();
while(isdigit(ch)) {
x = (x << 1) + (x << 3) + (ch ^ 48);
flag |= x >= pm ;
x %= pm;
ch = getchar();
}
if(flag)x+=pm;
}
ll phi(ll n){
ll ans = n,x=(ll)sqrt(n);
for(int i = 2;i <= x; ++ i){
if( ! (n % i)){
ans = ans / i * (i - 1);
while( ! (n % i)) n /= i;
}
}
if(n > 1) ans = ans / n * (n - 1);
return ans;
}
ll fastpower(ll a,ll b,ll c){
if( ! b) return 1 % c;
int ans = 1;
while(b){
if(b & 1) ans = ans * a % c;
a = a * a % c;
b >>= 1;
}
return ans;
}
int main(){
read(a);read(m);
pm = phi(m);
get(b);
ll ans = fastpower(a,b,m);
put(ans);
return 0;
}
矩乘
/********************
User:Mandy.H.Y
Language:c++
Problem:loj
********************/
//#include<bits/stdc++.h>
#include<stdio.h>
#include<cstring>
#include<iomanip>
#include<cmath>
using namespace std;
long long n,m;
int p[2][2];
int f[2];
void mulself(int a[2][2]){
int c[2][2]={{0,0},{0,0}};//初始化
for(int i = 0;i < 2 ; ++ i)
for(int k = 0;k < 2 ; ++ k)
for(int j = 0;j < 2 ; ++ j)
c[i][j] = (c[i][j] + (long long)a[i][k] * a[k][j] % m) % m;
memcpy(a,c,sizeof(c));
}
void multi(int a[2],int c[2][2]){
int g[2]={0,0};
for(int i = 0;i < 2; ++ i)
for(int j = 0;j < 2; ++ j)
g[j] = (g[j] + (long long)a[i] * c[i][j] % m ) % m;
memcpy(a,g,sizeof(g));
}
void qk_p(long long k) {
while(k) {
if(k&1) multi(f,p);
k>>=1;
mulself(p);
}
}
int main(){
read(n);
while(n != -1){
if(!n){//注意n可否为0;
puts("0");
read(n);
continue;
}
f[0]=1;f[1]=0;
p[0][0] = 1;p[0][1] = 1;
p[1][0] = 1;p[1][1] = 0;
m = 1e4;
qk_p(n-1);
put(f[0]);
puts("");
read(n);
}
return 0;
}
矩乘快速幂依托于结合律
广义矩乘:
求S到T刚好经过N条边的最短路
//矩乘快速幂用的是矩乘的结合律
//所以满足结合律的矩阵运算就可以用矩乘快速幂,广义矩乘
#include<bits/stdc++.h>
using namespace std;
const int maxn = 205;
int n,t,s,e,tot,cnt,size;
long long g[maxn][maxn];
long long a[maxn][maxn];
struct Tmp{
int u,id;
}tmp[maxn];
struct Edge{
int u,v,w;
}edge[maxn];
bool cmp(const Tmp &a,const Tmp &b){
return a.u<b.u;
}
void readdata(){
read(n);read(t);read(s);read(e);
for(int i = 1;i <= t; ++ i){
read(edge[i].w);
read(tmp[++tot].u);tmp[tot].id = i;
read(tmp[++tot].u);tmp[tot].id = i;
}
sort(tmp+1,tmp+tot+1,cmp);cnt = 0;
for(int i = 1;i <= tot; ++ i) {
if(tmp[i].u!=tmp[i-1].u){
if(!edge[tmp[i].id].u) edge[tmp[i].id].u = ++cnt;
else edge[tmp[i].id].v = ++cnt;
}else{
if(!edge[tmp[i].id].u) edge[tmp[i].id].u = cnt;
else edge[tmp[i].id].v = cnt;
}
if(tmp[i].u == s) s = cnt;
if(tmp[i].u == e) e = cnt;
}
memset(g,0x3f3f3f3f,sizeof(g));
// for(int i = 1;i <= cnt; ++ i) g[i][i] = 0;
//不能给自己到自己赋0,本题与经过的边数有关,
//赋为0相当于给自己加了一条0边
//答案会出错
for(int i = 1;i <= t; ++ i){
g[edge[i].u][edge[i].v] = min((long long)edge[i].w,g[edge[i].u][edge[i].v]);
g[edge[i].v][edge[i].u] = g[edge[i].u][edge[i].v];
}
}
void mulself(){
long long c[maxn][maxn];
memset(c,0x3f3f3f3f,sizeof(c));
for(int k = 1;k <= cnt; ++ k)
for(int i = 1;i <= cnt; ++ i)
for(int j = 1;j <= cnt; ++ j)
c[i][j] = min(c[i][j],g[i][k]+g[k][j]);
memcpy(g,c,sizeof(c));
}
void mul(){
long long c[maxn][maxn];
memset(c,0x3f3f3f3f,sizeof(c));
for(int k = 1;k <= cnt; ++ k)
for(int i = 1;i <= cnt; ++ i)
for(int j = 1;j <= cnt; ++ j)
c[i][j] = min(c[i][j],a[i][k]+g[k][j]);
memcpy(a,c,sizeof(c));
}
void work(){
memcpy(a,g,sizeof(g));
n--;
//不能给a[i][i]赋0,初值就这样,n记得-1
//毕竟不是真正的矩阵乘法,单位矩阵应该不适用了
while(n) {
if(n & 1) mul();
n >>= 1;
mulself();
}
put(a[s][e]);
}
int main(){
// file();
readdata();
work();
return 0;
}
欧拉函数
/***********************
User:Mandy
Language:c++
Problem:Visible Points
Algorithm:
***********************/
//特殊点(1,0),(0,1),(1,1)
//(0,0)本身不算
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 5;
int n,cnt;
long long phi[maxn];
int prime[maxn];
bool vis[maxn];
template<class T>inline void read(T &x){
x = 0;char ch = getchar();bool flag = 0;
while(!isdigit(ch)) flag |= ch == '-',ch = getchar();
while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();
if(flag) x = -x;
}
template<class T>void putch(const T x){if(x > 9) putch(x / 10);putchar(x % 10 | 48);}
template<class T>void put(const T x){if(x < 0) putchar('-'),putch(-x);else putch(x);}
void readdata(){
read(n);
}
void work(int t){
put(t);putchar(' ');
put(n);putchar(' ');
put(phi[n]*2+3);putchar('\n');
}
void get_Euler(){
for(int i = 2;i <= 1000; ++ i){
if(!vis[i]) prime[++cnt] = i,phi[i] = i - 1;
for(int j = 1;j <= cnt && prime[j] * i <= 1000; ++ j){
int now = prime[j] * i;vis[now] = 1;
if(i % prime[j] == 0){
phi[now] = phi[i] * prime[j];
break;
} else phi[now] = phi[i] * (prime[j] - 1);
}
}
}
int main(){
int c;read(c);
get_Euler();
for(int i = 2;i <= 1000; ++ i) phi[i] += phi[i - 1];
for(int i = 1;i <= c; ++ i){
readdata();
work(i);
}
return 0;
}
线性筛
void get_prime(){
for(int i = 2;i <= n; ++ i){
if(!vis[i]) prime[++cnt] = i;
for(int j = 1;j <= cnt && (long long)prime[j] * i <= n; ++j){
vis[prime[j] * i] = 1;
if(i % prime[j] == 0) break;
}
}
}
算数基本定理
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e4 + 5;
const int mod = 9901;
long long a,b,ans;
long long p[maxn][2],cnt;
void readdata(){
read(a);read(b);
}
long long ksm(long long x,long long P){
long long ans = 1;
while(P){
if(P & 1) ans = ans * x % mod;
P >>= 1;
x = x * x % mod;
}
return ans;
}
long long inv(long long x){
return ksm(x,mod - 2);
}
void work(){
int x = sqrt(a);
long long cur = a;
if(a == 0) {//!!!!!!!!!!!!!!!
puts("0");
return;
}
for(int i = 2;i <= x; ++ i){
if(cur % i == 0) {
p[++cnt][0] = i;
while(cur % i == 0) cur /= i,p[cnt][1]++;
}
}
if(cur > 1) p[++cnt][0] = cur,p[cnt][1] = 1;
//等比数列,算数基本定理推论
ans = 1;//!!!!!!
for(int i = 1;i <= cnt; ++ i){
if((p[i][0] - 1) % mod != 0) cur = (ksm(p[i][0],p[i][1] * b + 1) - 1 + mod) * inv(p[i][0] - 1 + mod) % mod;
else cur = (b * p[i][1] + 1) % mod;
ans = ans * cur % mod;
}
put(ans);
}
int main(){
readdata();
work();
return 0;
}
整除分块
/***************
User:Mandy
Language:c++
Problem:AcWing
Algorithm:
***************/
#include<bits/stdc++.h>
using namespace std;
long long n,k;
long long ans = 0;
void readdata(){
read(n);read(k);
ans = n * k;
}
void work(){
for(int i = 1,j;i <= min(k,n); i = j+1){
j = min(k/(k/i),n);
//j>k时k/i = 0;
//j必须小于n
ans -= ((long long)k/i)*((long long)i+j)*((long long)j-i+1)/2;
}
put(ans);
}
int main(){
// file();
readdata();
work();
return 0;
}
出栈sixways
//认真看,杜绝抄袭
//好好消化一下,这题很经典
//记忆化搜索/递归 做法
#include<cstdio>
#define MAX_N 20
#define ll long long
using namespace std;
int n;
ll f[MAX_N][MAX_N];
ll dfs(int i,int j)
{
if(f[i][j]) return f[i][j];
if(i==0)return 1; //边界
if(j>0) f[i][j]+=dfs(i,j-1);
f[i][j]+=dfs(i-1,j+1);
return f[i][j];
}
int main()
{
scanf("%d",&n);
printf("%lld",dfs(n,0));
return 0;
}
//递归转递推 递推做法
#include<cstdio>
#define MAX_N 20
#define ll long long
using namespace std;
int n;
ll f[MAX_N][MAX_N];
int main()
{
scanf("%d",&n);
for(int i=0;i<=n;i++)
{
f[0][i]=1;
}
for(int i=1;i<=n;i++)
{
for(int j=i;j<=n;j++)
{
if(i==j)f[i][j]=f[i-1][j];
else f[i][j]=f[i][j-1]+f[i-1][j];
}
}
printf("%lld",f[n][n]);
return 0;
}
//数论做法 卡特兰数
//公式1:
#include<cstdio>
#define MAX_N 20
#define ll long long
using namespace std;
int n;
ll f[MAX_N];
int main()
{
f[0]=f[1]=1;
scanf("%d",&n);
for(int i=2;i<=n;i++)
{
for(int j=0;j<i;j++)
{
f[i]+=f[j]*f[i-j-1];
}
}
printf("%lld",f[n]);
return 0;
}
//公式2:
#include<cstdio>
#define MAX_N 20
#define ll long long
using namespace std;
int n;
ll f[MAX_N];
int main()
{
f[0]=f[1]=1;
scanf("%d",&n);
for(int i=2;i<=n;i++)
{
f[i]+=f[i-1]*(4*i-2)/(i+1);
}
printf("%lld",f[n]);
return 0;
}
//公式3:
#include<cstdio>
#define MAX_N 20
#define ll long long
using namespace std;
int n;
ll c[MAX_N*2][MAX_N];
int main(){
scanf("%d",&n);
for(int i=1;i<=2*n;i++)
{
c[i][0]=c[i][i]=1;
for(int j=1;j<i;j++)
{
c[i][j]=c[i-1][j]+c[i-1][j-1];
}
}
printf("%lld",c[2*n][n]/(n+1));
return 0;
}
//公式4:
#include<cstdio>
#define MAX_N 20
#define ll long long
using namespace std;
int n;
ll c[MAX_N*2][MAX_N];
int main(){
scanf("%d",&n);
for(int i=1;i<=2*n;i++)
{
c[i][0]=c[i][i]=1;
for(int j=1;j<i;j++)
{
c[i][j]=c[i-1][j]+c[i-1][j-1];
}
}
printf("%lld",c[2*n][n]-c[2*n][n-1]);
return 0;
}
//高精/打表:
#include<iostream>
#include<cstdio>
#include<cstring>
#define MAX_N 110
using namespace std;
int f[MAX_N][MAX_N],c[MAX_N];
inline int len(int a[])
{
int i;
for(i=60;i>=0;i--)//想要100个以上,这个i的范围要改
{
if(a[i]!=0) break;
}
return i;
}
inline void add(int a[],int b[],int w)//高精加法
{
int lena=len(a),lenb=len(b);
for(int i=0;i<=max(lena,lenb);i++)
{
f[w][i]=a[i]+b[i];
}
for(int i=0;i<=max(lena,lenb)+1;i++)
{
f[w][i+1]+=f[w][i]/10;
f[w][i]%=10;
}
}
inline void Catalan(int a[],int b[])//卡特兰
{
memset(c, 0, sizeof(c));
int lena=len(a),lenb=len(b);
for (int i=0;i<=lena;i++){
for (int j=0;j<=lenb;j++)
c[i+j]+=a[i]*b[j];
}
for (int i=0;i<=lena+lenb+1;i++)
{
c[i+1]+=c[i]/10;
c[i]%=10;
}
}
int main()
{
//int k;
freopen("Catalan.txt","w"stdin);//文件操作;
f[0][0]=f[1][0]=1;
for (int i=2;i<=100;i++)//同理,要多输出几个i就等于几
{
for (int j=0;j<i;j++)
{
Catalan(f[j], f[i-j-1]);
add(f[i],c,i);
}
}
for(int i=1;i<=100;i++)//输出 卡特兰数 1-100,范围同上,要输出几个自己改
{
for (int j=len(f[i]);j>=0;j--)
{
//printf("%d",f[i][j]);
putchar((char)f[i][j]+'0');//比printf稍快?
}
printf("\n");
}
return 0;
}
01规划
最优比率生成树
/********************
User:Mandy
Language:c++
Problem:
********************/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double dou;
const int maxn = 1005;
const dou esp = 1e-8;
int t,n,size;
double d[maxn];
int from[maxn];
double dis[maxn][maxn],cost[maxn][maxn];
struct Node{
int x,y,z;
}node[maxn];
void readdata(){
for(int i = 1;i <= n; ++ i){
read(node[i].x);
read(node[i].y);
read(node[i].z);
}
}
double dist(int i,int j){
return sqrt(((dou)node[i].x - node[j].x) *
((dou)node[i].x - node[j].x) +
((dou)node[i].y - node[j].y) *
((dou)node[i].y - node[j].y));
}
void init(){
for(int i = 1;i <= n; ++ i){
for(int j = i;j <= n; ++ j){
dis[i][j] = dist(i,j);
dis[j][i] = dis[i][j];
cost[i][j] = abs((double)node[i].z - node[j].z);
cost[j][i] = cost[i][j];
}
}
}
double Prim(double r){
int cnt = 1;t = 0;double c = 0,D = 0;
for(int i = 1;i <= n;++i)
d[i] = cost[1][i]-r*dis[1][i],from[i] = 1;
d[1] = -2e9;
for(int j = 1;j <= n; ++ j){
double cur = 2e9;int id = 0;
for(int i = 1;i <= n; ++ i){
if(d[i]!=-2e9 && d[i] < cur){
cur = d[i]; id = i;
}
}
t += cur;cnt++;d[id] = -2e9;
c += cost[from[id]][id];
D += dis[from[id]][id];
if(cnt == n) break;
for(int i = 1;i <= n; ++ i) {
if(d[i] > cost[id][i]-r*dis[id][i]){
d[i] = cost[id][i]-r*dis[id][i];
from[i] = id;
}
}
}
if(t < esp) return c/D;
else return -2e9;
}
void work(){
double ans = (double)7;
while(1){
double R = Prim(ans);
if(R == -2e9||fabs(ans - R) <= esp) break;//fabs!!
//直接判断是否相等就好,r不会等于-2e9
ans = R;
}
printf("%.3lf\n",ans);
}
int main(){
// file();
while(~scanf("%d",&n)){
if(!n) break;
size = 0;
readdata();
init();
work();
}
return 0;
}
Gauss
/*************************
User:Mandy.H.Y
Language:c++
Problem: luogu3389
Algorithm:Gauss
*************************/
#include<bits/stdc++.h>
using namespace std;
const int maxn=105;
const double eps=1e-8;
int n;
double a[maxn][maxn];
void readdata()
{
read(n);
for(int i=1;i<=n;++i)
for(int j=1;j<=n+1;++j)
scanf("%lf",&a[i][j]);
}
double fa(double b)
{
if(b<0) return -b;
else return b;
}
void work()
{
for(int i=1;i<=n;++i){
int maxi=i;
for(int j=i+1;j<=n;++j)
if(a[j][i]>a[maxi][i]) maxi=j;
if(maxi!=i) swap(a[i],a[maxi]);
if(fa(a[i][i])<eps){
printf("No Solution");
return;
}
double d=a[i][i];
for(int j=i;j<=n+1;++j) a[i][j]/=d;
for(int j=i+1;j<=n+1;++j){
d=a[j][i];
for(int k=i;k<=n+1;++k)
a[j][k]-=a[i][k]*d;
}
}
for(int i=n-1;i>=1;--i)
for(int j=i+1;j<=n;++j)
a[i][n+1]-=a[j][n+1]*a[i][j];
for(int i=1;i<=n;++i) printf("%.2lf\n",a[i][n+1]);
}
int main()
{
// docu();
readdata();
work();
return 0;
}
/*************************
User:Mandy.H.Y
Language:c++
Problem: Gauss
Algorithm:Gauss
*************************/
#include <bits/stdc++.h>
using namespace std;
const int maxn = 25;
const double eps = 1e-8;
int n;
double a[maxn][maxn];
double b[maxn * maxn];
double x[maxn * maxn];
void readdata() {
int cnt = 0;
while (cin >> b[cnt])
++ cnt;
n = int(sqrt(cnt));
for (int i = 0; i < n; ++i)
for (int j = 0; j <= n; ++j) {
a[i][j] = b[i * (n+1) + j];
}
}
double fa(double b) {
if (b < 0)
return -b;
else
return b;
}
int Gauss(int m, int n) {
int col = 0, k = 0;
for (; k < m && col < n; ++k, ++col) {
int r = k;
for (int i = k + 1; i < m; ++i)
if (fa(a[i][col]) > fabs(a[r][col]))
r = i;
if (fa(a[r][col]) < eps) {
k--;
continue;
}
if (r != k)
for (int i = col; i <= n; ++i)
swap(a[k][i], a[r][i]);
for (int i = k + 1; i < m; ++i)
if (fa(a[i][col]) > eps) {
double t = a[i][col] / a[k][col];
for (int j = col; j <= n; j++)
a[i][j] -= a[k][j] * t;
a[i][col] = 0;
}
}
for (int i = k ; i < m ; ++i)
if (fa(a[i][n]) > eps)
return -1;
if (k < n)
return n - k;
for (int i = n - 1; i >= 0; i--) {
double temp = a[i][n];
for (int j = i + 1; j < n; ++j)
temp -= x[j] * a[i][j];
x[i] = (temp / a[i][i]);
}
return 0;
}
void work() {
/*
for (int i = 1; i <= n; ++i) {
int maxi = i;
for (int j = i + 1; j <= n; ++j)
if (a[j][i] > a[maxi][i])
maxi = j;
if (maxi != i)
swap(a[i], a[maxi]);
if (fa(a[i][i]) < eps) {
if (fa(a[i][n + 1]) < eps)
printf("No unique solution!");
else
printf("No solution!");
return;
}
double d = a[i][i];
for (int j = i; j <= n + 1; ++j)
a[i][j] /= d;
for (int j = i + 1; j <= n + 1; ++j) {
d = a[j][i];
for (int k = i; k <= n + 1; ++k)
a[j][k] -= a[i][k] * d;
}
}
for (int i = n - 1; i >= 1; --i)
for (int j = i + 1; j <= n; ++j)
a[i][n + 1] -= a[j][n + 1] * a[i][j];
*/
int num = Gauss(n, n);
if (num == -1)
printf("No solution!");
else if (num)
printf("No unique solution!");
else
for (int i = 0; i < n; ++i)
printf("%.3lf ", x[i]);
}
int main() {
// docu();
readdata();
work();
return 0;
}
图论
差分约束
主要是建图,看看例题就好
SPFA
while(l != r){
int u = q[l];
l++;
l %= mod;
vis[u] = 0;
if(cnt[u] > n) {
puts("successful conspiracy");
return;
}
int rr = (r-1+mod)%mod;
if(l != r) if(dis[q[l]] < dis[q[rr]]) swap(q[l],q[rr]);
for(int i = first[u];i;i = edge[i].nt){
int v = edge[i].v;
int w = edge[i].w;
if(dis[v] < dis[u] + w){
dis[v] = dis[u] + w;
cnt[v] = cnt[u] + 1;
if(!vis[v]){
vis[v] = 1;
q[r++] = v;
r %= mod;
int rr = (r-1+mod)%mod;
if(l != r) if(dis[q[l]] < dis[q[rr]]) swap(q[l],q[rr]);
}
}
}
}
次短路就维护次大值
01bfs双端队列
动态加点
//动态加点
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e4 + 5;
const int maxm = 1e5 + 5;
int n,m,size,first[maxn];
int dis[maxn],ans = 0x3f3f3f3f;
bool vis[maxn];
int q[maxn + 5],l,r;
struct Edge{
int v,nt,w;
}edge[maxm << 1];
struct EE{
int u,v,a,b;
EE(int _u = 0,int _v = 0,int _a = 0,int _b = 0):u(_u),v(_v),a(_a),b(_b){}
}E[maxm << 1];
void eadd(int u,int v,int w){
edge[++size].v = v;edge[size].w = w;
edge[size].nt = first[u];first[u] = size;
}
void readdata() {
read(n);read(m);
for(int i = 1;i <= m; ++ i) {
int u,v,a,b;read(u);read(v);read(a);read(b);
E[i] = EE(u,v,a,b);
}
}
bool cmp(const EE &x,const EE &y){
return x.a<y.a;
}
void spfa(int s,int t){
l = r = 0;q[r++]=s;q[r++]=t;
vis[s] = 1;vis[t] = 1;
while(l != r){
int u = q[l];++l;l = l%maxn;vis[u] = 0;
int rr=(r-1+maxn)%maxn;if(dis[q[rr]]<dis[q[l]]) swap(q[l],q[rr]);
for(int i = first[u];i;i = edge[i].nt){
int v = edge[i].v,w = edge[i].w;
if(dis[v]>max(dis[u],w)){
dis[v]=max(dis[u],w);
if(!vis[v]){
vis[v] = 1;q[r++] = v;r%=maxn;
int rr=(r-1+maxn)%maxn;if(dis[q[rr]]<dis[q[l]]) swap(q[l],q[rr]);
}
}
}
}
}
void work(){
sort(E+1,E+m+1,cmp);memset(dis,0x3f3f3f3f,sizeof(dis));
dis[1] = 0;
for(int i = 1;i <= m; ++ i){
eadd(E[i].u,E[i].v,E[i].b);eadd(E[i].v,E[i].u,E[i].b);
spfa(E[i].u,E[i].v);ans = min(ans,dis[n]+E[i].a);
}
if(ans<0x3f3f3f3f)put(ans);
else puts("-1");
}
int main(){
file();
readdata();
work();
return 0;
}
分层图意会
负环
bool SPFA(int s){
l = r = 0;
q[r ++ ] = s;
while(l != r){
int u = q[l];
l++;
used[u] = 1;
l = l % 1000;
int rr = (r - 1 + 1000) % 1000;
if(l != r) if(dis[q[l]] > dis[q[rr]]) swap(q[l],q[rr]);
vis[u] = 0;
if(cnt[u] > n) return 1;
for(int i = first[u];i;i = edge[i].nt){
int v = edge[i].v;
int w = edge[i].w;
if(dis[u] + w < dis [v]){
dis[v] = dis[u] + w;
cnt[v] = cnt[u] + 1;
if(!vis[v]){
q[r++] = v;
vis[v] = 1;
r %= 1000;
int rr = (r - 1 + 1000) % 1000;
if(l != r) if(dis[q[l]] > dis[q[rr]]) swap(q[l],q[rr]);
}
}
}
}
return 0;
}
最短路计数
void work()
{
queue<int>q;
q.push(1);
t[1]=1;
memset(d,0x3f3f3f,sizeof(d));
d[1]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
v[u]=0;
for(int i=f[u];i!=0;i=e[i].nt)
{
int v1=e[i].v;
if(d[v1]>d[u]+1)
{
t[v1]=t[u]%100003;
d[v1]=d[u]+1;
if(!v[v1])
{
v[v1]=1;
q.push(v1);
}
}
else if(d[v1]==d[u]+1)
{
t[v1]=(t[v1]+t[u])%100003;
if(!v[v1])
{
v[v1]=1;
q.push(v1);
}
}
}
}
for(int i=1;i<=n;i++)
{
printf("%d\n",t[i]%100003);
}
}
Dijkstra
void work()
{
memset(d,0x3f3f3f3f,sizeof(d));
d[s]=0;
q.push(make_pair(0,s));
while(!q.empty())
{
int u=q.top().second;q.pop();
if(vis[u]) continue;vis[u]=1;
for(int i=f[u];i;i=e[i].nt){
int v=e[i].v,w=e[i].w;
if(d[u]+w<d[v]){
d[v]=d[u]+w;q.push(make_pair(d[v],v));
}
}
}
}
Floyd
传递闭包
/*********************
User:Mandy
Language:c++
Problem:AcWing
Algorithm:
*********************/
//其实这道题正解是topsort
//天知道我拿到这一道题第一反应是并查集
// 二分 + 传递闭包
//这道题是真的坑,题目的意思是求最少的关系数确定的关系
//也就是说求出最小的t使得不等式确定大小关系或者得出矛盾
//重点在优先求最小的t
//其实可以从头到尾枚举,每一次做一遍floyd
#include<bits/stdc++.h>
using namespace std;
const int maxn = 30;
int n,m;
char c[1005][5];
char s1[maxn];
bool g[maxn][maxn];
struct Cnt{
int c,id;
}cnt[maxn];
void floyd(int r){
memset(g,0,sizeof(g));
for(int i = 1;i <= r; ++ i)
g[c[i][0]-'A'][c[i][2]-'A'] = 1;
for(int k = 0;k < n; ++ k)
for(int i = 0;i < n; ++ i)
for(int j = 0;j < n; ++ j)
g[i][j] |= g[i][k] & g[k][j];
}
int check(){
bool judge = 0;
for(int i = 0;i < n; ++ i){
for(int j = i;j < n; ++ j){
if(g[i][j] == 1 && g[j][i] == 1){
return 0;//有矛盾 优先
}
if(i != j && g[i][j] == 0 && g[j][i] == 0){
judge = 1;//不确定不优先,万一有矛盾呢
}
}
}
if(judge) return 1;//无矛盾,不确定
else return 2;//无矛盾,确定
}
bool cmp(const Cnt &x,const Cnt &y){
return x.c>y.c;
}
void work(){
memset(s1,0,sizeof(s1));
for(int i = 1;i <= m; ++ i)
scanf("%s",c[i]);
floyd(m);
int judge = check();
if(judge == 1) {
puts("Sorted sequence cannot be determined.");
return;//所有m个关系都不能确定,直接输出
}
int l = 0,r = m,ans = m;
while(l <= r){
int mid = (l + r)>>1;
floyd(mid);
int x = check();
if(x != 1) {//只要关系不是不确定的就可以
if(s1[0]==0 && x == 2){//关系确定,且还没求过序列
memset(cnt,0,sizeof(cnt));
for(int i = 0;i < n; ++ i){
for(int j = 0;j < n; ++ j)
if(g[i][j]) cnt[i].c++;
cnt[i].id = i;
}
sort(cnt,cnt+n,cmp);//按小于的数的个数排序
for(int i = 0;i<n; ++ i) s1[i] = cnt[i].id+'A';
}
judge = x,ans = mid,r = mid-1;
//judge负责记录最小的t求出的关系是大小关系还是矛盾
}
else l = mid+1;
}
if(judge == 0) printf("Inconsistency found after %d relations.\n",ans);
else {
printf("Sorted sequence determined after %d relations: %s.\n",ans,s1);
}
}
int main(){
while(~scanf("%d%d",&n,&m) && (n || m))
work();
return 0;
}
最短路
for(int k = 1;k <= n; ++ k)
for(int i = 1;i <= n; ++ i)
for(int j = 1;j <= n; ++ j)
if(dis[i][j] > dis[i][k] + dis[k][j])
dis[i][j] = dis[i][k] + dis[k][j];
最小环
/*********************
User:Mandy
Language:c++
Problem:AcWing
Algorithm:
*********************/
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cstring>
using namespace std;
const int maxn = 105;
int n,m;
int g[maxn][maxn];
int a[maxn][maxn];
int pos[maxn][maxn];
int path[maxn],cnt = 0;
void readdata(){
read(n);read(m);
memset(g,0x3f3f3f3f,sizeof(g));//存初始边
memset(a,0x3f3f3f3f,sizeof(a));//存最短路
for(int i = 1;i <= n; ++ i) a[i][i] = 0;//记得赋初值
//杜绝了pos[i][i]有值的可能性
for(int i = 1; i <= m; ++ i){
int u,v,l;read(u);read(v);read(l);
g[u][v] = min(g[u][v],l);//有重边
g[v][u] = g[u][v];
a[u][v] = a[v][u] = g[u][v];
}
}
void get_path(int i,int j){
int k = pos[i][j];
if(!k) return;
else{
get_path(i,k);
path[++cnt] = k;
get_path(k,j);
}
}
void work(){
int ans = 0x3f3f3f3f;
for(int k = 1;k <= n; ++ k){//枚举环上最大的点
for(int i = 1;i < k; ++ i){
for(int j = i+1;j < k; ++ j){ //三个点两两不同
if((long long)a[i][j]+g[i][k]+g[k][j] < ans){//开long long
ans = a[i][j]+g[i][k]+g[k][j];
cnt = 0;
path[++cnt] = i;get_path(i,j);
path[++cnt] = j;path[++cnt] = k;//k与i,j都直接相连
}
}
}
for(int i = 1;i <= n; ++ i){
for(int j = 1;j <= n; ++ j){
if(a[i][j] > a[i][k] + a[k][j]){
a[i][j] = a[i][k] + a[k][j];
a[j][i] = a[i][j];
pos[i][j] = k;pos[j][i] = k;
}
}
}
}
if(cnt == 0){
puts("No solution.");
return;
}
int start = 0,cur = n+1;
for(int i = 1;i <= cnt; ++ i){
if(path[i]<cur) cur = path[i],start = i;
}
printf("%d ",path[start]);
int i = start+1;if(i>cnt)i=1;
while(i != start){
printf("%d ",path[i]);
i++;if(i>cnt)i=1;
}
}
int main(){
// file();
readdata();
work();
return 0;
}
线段树优化建图
/*******************
User:Mandy
Language:c++
Problem:luogu
Algorithm:
*******************/
//线段树优化建图
//听说无向图建一棵就好,有向图建两颗
#include<bits/stdc++.h>
#define lson tree[now].ls,l,mid
#define rson tree[now].rs,mid+1,r
using namespace std;
typedef long long ll;
const int maxm = 2e6 + 5;
const int maxn = 2e5 + 5;
//大概一棵树就有nlogn条边,两棵树乘以二
int n,q,size,tot,s,rootin,rootout;
int mu,mv,ml,mr,first[maxn << 1];
bool vis[maxn << 1];//注意数组大小
long long mw,dis[maxn << 1];
struct Edge{
int v,nt;
long long w;
}edge[maxm << 1];
struct SegmentTree{
int ls,rs;
}tree[maxn << 1];
template<class T>inline void read(T &x){
x = 0;char ch = getchar();bool flag = 0;
while(!isdigit(ch)) flag |= ch == '-',ch = getchar();
while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();
if(flag) x = -x;
}
template<class T>void putch(const T x){if(x > 9) putch(x / 10);putchar(x % 10 | 48);}
template<class T>void put(const T x){if(x < 0) putchar('-'),putch(-x);else putch(x);}
void eadd(int u,int v,long long w){
edge[++size].v = v;
edge[size].w = w;
edge[size].nt = first[u];
first[u] = size;
}
void buildin(int &now,int l,int r){
if(l == r) {now = l;return;}
now = ++tot;
int mid = (l + r) >> 1;
buildin(lson);buildin(rson);
eadd(now,tree[now].ls,0);eadd(now,tree[now].rs,0);
}
void buildout(int &now,int l,int r){
if(l == r) {now = l;return;}
now = ++tot;
int mid = (l + r) >> 1;
buildout(lson);buildout(rson);
eadd(tree[now].ls,now,0);eadd(tree[now].rs,now,0);
}
void modifyin(int now,int l,int r,int x,int y){
if(x <= l && r <= y) {eadd(mv,now,mw);return;}
int mid = (l + r) >> 1;
if(x <= mid) modifyin(lson,x,y);
if(y > mid) modifyin(rson,x,y);
}
void modifyout(int now,int l,int r,int x,int y){
if(x <= l && r <= y) {eadd(now,mv,mw);return;}
int mid = (l + r) >> 1;
if(x <= mid) modifyout(lson,x,y);
if(y > mid) modifyout(rson,x,y);
}
void readdata(){
read(n);read(q);read(s);
tot = n;//注意初始化
buildin(rootin,1,n);buildout(rootout,1,n);
for(int i = 1;i <= q; ++ i){
int opt;read(opt);
switch(opt){
case 1:{read(mu);read(mv);read(mw);eadd(mu,mv,mw);break;}
case 2:{read(mv);read(ml);read(mr);read(mw);modifyin(rootin,1,n,ml,mr);break;}
case 3:{read(mv);read(ml);read(mr);read(mw);modifyout(rootout,1,n,ml,mr);break;}
}
}
}
void dijkstra(){
typedef pair<long long,int>pir;
priority_queue<pir,vector<pir>,greater<pir> >q;
memset(dis,0x3f,sizeof(dis));
q.push(make_pair(0ll,s));dis[s] = 0;
while(!q.empty()){
int u = q.top().second;q.pop();
if(vis[u]) continue;vis[u] = 1;
for(int i = first[u];i;i = edge[i].nt){
int v = edge[i].v;long long w = edge[i].w;
if(dis[v] > dis[u] + w){
dis[v] = dis[u] + w;
q.push(make_pair(dis[v],v));
}
}
}
}
void work(){
dijkstra();
for(int i = 1;i <= n; ++ i){
if(dis[i] < 4e17) put(dis[i]),putchar(' ');
else put(-1),putchar(' ');
}
}
int main(){
readdata();
work();
return 0;
}
缩点
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 5;
const int maxm = 5e4 + 5;
int n,m,size,first[maxn],tot,tp,cnt;
int low[maxn],dfn[maxn],s[maxn],a[maxn];
int father[maxn],d[maxn];
bool vis[maxn];
struct Edge{
int u,v,nt;
}edge[maxm];
//char *TT,*mo,but[(1 << 15) + 2];
//#define getchar() ((TT == mo && (mo = ((TT = but) + fread(but,1,1 << 15,stdin)),TT == mo)) ? -1 : *TT++)
template<class T>inline void read(T &x){
x = 0;char ch = getchar();bool flag = 0;
while(!isdigit(ch)) flag |= ch == '-',ch = getchar();
while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();
if(flag) x = -x;
}
template<class T>void putch(const T x){
if(x > 9) putch(x / 10);
putchar(x % 10 | 48);
}
template<class T>void put(const T x){
if(x < 0) putchar('-'),putch(-x);
else putch(x);
}
void eadd(int u,int v){
edge[++ size].v = v;
edge[size].u = u;
edge[size].nt = first[u];
first[u] = size;
}
void tarjan(int u){
dfn[u] = low[u] = ++tot;
vis[u] = 1;
s[tp++] = u;
for(int i = first[u];i;i = edge[i].nt){
int v = edge[i].v;
if(!dfn[v]){
tarjan(v);
low[u] = min(low[v],low[u]);
}else if(vis[v]) low[u] = min(low[u],dfn[v]);
}
if(low[u] == dfn[u]){
int x;++cnt;
do{
x = s[tp - 1];
--tp;
father[x] = cnt;
a[cnt] ++;
vis[x] =0;
}while(x != u);
}
}
int main(){
read(n);read(m);
for(int i = 1;i <= m; ++i){
int u,v;
read(u);read(v);
eadd(u,v);
}
for(int i = 1;i <= n; ++ i){
if(!dfn[i]) tarjan(i);
}
for(int i = 1;i <= m; ++ i){
int u = edge[i].u;
int v = edge[i].v;
if(father[u] != father[v]){
d[father[u]]++;
}
}
int ans = 0;
int num = 0;
for(int i = 1;i <= cnt;++i){
if(!d[i]){
ans = a[i];
num ++;
}
if(num >= 2) {
puts("0");
return 0;
}
}
put(ans);
return 0;
}
割点
#include<bits/stdc++.h>
#define M 200002
#define N 20002
#define Max(x,y) (x)>(y)?(x):(y)
#define Min(x,y) (x)<(y)?(x):(y)
#define F(i,l,r) for(int i=l;i<=r;++i)
using namespace std;
static int n,m,dfn[N],size=0,low[N],cnt=0,tot=0,f[N];
bool vis[N]={0};
struct E
{
int v,nt;
};
static E e[M];
void eadd(int u,int v)
{
e[++size].v=v;
e[size].nt=f[u];
f[u]=size;
}
void readdata()
{
n=read(); m=read();
F(i,1,m)
{
int x=read(),y=read();
eadd(x,y);
eadd(y,x);
}
}
void tarjan(int u,int fa)
{
dfn[u]=low[u]=++cnt;
int cd=0;
for(int i=f[u];i;i=e[i].nt)
{
int v=e[i].v;
if(!dfn[v])
{
if(u==fa) ++cd;
tarjan(v,u);
low[u]=Min(low[u],low[v]);
if((u==fa&&cd>=2&&!vis[fa])||(u!=fa&&dfn[u]<=low[v]&&!vis[u])) ++tot,vis[u]=1;
}
else if(v!=fa) low[u]=Min(low[u],dfn[v]);
}
// if((u==fa&&cd>=2&&!vis[fa])||(u!=fa&&dfn[fa]<=low[u]&&!vis[u])) ++tot,vis[fa]=1;
}
void work()
{
F(i,1,n)
{
if(!dfn[i]) tarjan(i,i);
}
put(tot);
putchar('\n');
F(i,1,n) if(vis[i]) put(i),putchar(' ');
}
int main()
{
// init();
readdata();
work();
return 0;
}
桥
看书吧
Topsort
这个就算了吧,栈和队列都可以
最小生成树
kruskal
int main(){
read(n);read(m);
for(int i =1;i <= n; ++ i) father[i] = i;
for(int i = 1;i <= m;++i){
int u,v,w;
read(u);read(v);read(w);
eadd(u,v,w);
}
sort(edge + 1,edge + m + 1);
for(int i =1;i <= m; ++ i){
int u = edge[i].u;
int v = edge[i].v;
int w = edge[i].w;
if(find(u) != find(v)){
merge(u,v);
++cnt;
ans += (long long)w;
}
if(cnt == n - 1) break;
}
printf("%lld",ans);
return 0;
}
prim
void work(){
memset(dis,0x3f3f3f3f,sizeof(dis));
dis[1] = 0;
priority_queue<Node>q;
q.push(Node(1,0));
while(!q.empty()){
Node e = q.top();
q.pop();
int u = e.v,w = e.w;
if(vis[u]) continue;
vis[u] = 1;
++cnt;
ans += (long long)w;
if(cnt == n) break;
for(int i = first[u];i;i = edge[i].nt){
int v = edge[i].v;
int w = edge[i].w;
if((!vis[v]) && w < dis[v]){
dis[v] = w;
q.push(Node(v,w));
}
}
}
printf("%lld",ans);
}
n^2
#include<bits/stdc++.h>
using namespace std;
int n,m,d[5005],p,t,s,h[5005];
struct ee
{
int u,v,w,nt;
}e[400005];
void eadd(int x,int y,int z)
{
e[++s].u=x;
e[s].v=y;
e[s].w=z;
e[s].nt=h[x];
h[x]=s;
}
void init()
{
freopen("1.txt","r",stdin);
}
void readdata()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
eadd(x,y,z);
eadd(y,x,z);
}
}
void work()
{
memset(d,0x3f,sizeof(d));
d[1]=0;
for(int i=h[1];i;i=e[i].nt)
{
if(e[i].w<d[e[i].v])//重边
d[e[i].v]=e[i].w;
}
for(int i=1;i<n;i++)
{
p=2000000000;
int x;
for(int j=2;j<=n;j++)
{
if(d[j]&&d[j]<p)
{
p=d[j];
x=j;
}
}
t+=p;
d[x]=0;
for(int j=h[x];j;j=e[j].nt) if(d[e[j].v]>e[j].w) d[e[j].v]=e[j].w;
}
printf("%d",t);
}
int main()
{
// init();
readdata();
work();
return 0;
}
瓶颈生成树,最小瓶颈路,记住结论
严格次小生成树
/**********************
User:Mandy
Language:c++
Problem:luogu
Algorithm:
**********************/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
const int maxm = 3e5 + 5;
int n,m,size,first[maxn],fa[maxn],dep[maxn];
int father[maxn][20],val[maxn][20][2];
bool vis[maxm];
long long ans,sum;
int val1,val2;
struct Edge{int v,u,w;}E[maxm];
struct EE{int v,nt,w;}edge[maxm << 1];
bool cmp(const Edge &a,const Edge &b) {return a.w < b.w;}
int find(int x) {return fa[x] == x ? x : fa[x] = find(fa[x]);}
void eadd(int u,int v,int w) {edge[++size].v=v;edge[size].w=w;edge[size].nt=first[u];first[u]=size;}
void readdata(){
read(n);read(m);
for(int i = 1;i <= m; ++ i){read(E[i].u);read(E[i].v);read(E[i].w);}
}
void dfs(int u,int f){
father[u][0] = f;
for(int i = 1;i <= 19; ++ i) {
int ff = father[u][i-1],j = i-1;father[u][i] = father[ff][j];
if(val[u][j][0] == val[ff][j][0]){
val[u][i][0] = val[u][j][0];
val[u][i][1] = max(val[u][j][1],val[ff][j][1]);
} else {
if(val[u][j][0] > val[ff][j][0]){
val[u][i][0] = val[u][j][0];
val[u][i][1] = max(val[ff][j][0],val[u][j][1]);
} else {
val[u][i][0] = val[ff][j][0];
val[u][i][1] = max(val[u][j][0],val[ff][j][1]);
}
}
}
for(int i = first[u];i;i = edge[i].nt){
int v = edge[i].v;if(v == f) continue;
val[v][0][0] = edge[i].w;dep[v] = dep[u]+1;dfs(v,u);
}
}
void get_max(int u,int v){
val1 = -2e9,val2 = -2e9;
if(dep[u]<dep[v]) swap(u,v);
for(int i = 19;i >= 0; -- i){
if(dep[father[u][i]]>=dep[v]){
if(val[u][i][0]>=val1){//大于等于,不能让次大值等于最大值
val1 = val[u][i][0];
val2 = max(val2,val[u][i][1]);
}else val2 = max(val2,val[u][i][0]);
u = father[u][i];
}
}
if(u == v) return;//!!!!!!
for(int i = 19;i >= 0; -- i){
if(father[u][i] != father[v][i]){
if(val[u][i][0]>=val1){
val1 = val[u][i][0];
val2 = max(val2,val[u][i][1]);
}else val2 = max(val2,val[u][i][0]);
u = father[u][i];
if(val[v][i][0]>=val1){
val1 = val[v][i][0];
val2 = max(val2,val[v][i][1]);
}else val2 = max(val2,val[v][i][0]);
v = father[v][i];
}
}
if(val[u][0][0]>=val1){
val1 = val[u][0][0];
val2 = max(val2,val[u][0][1]);
}else val2 = max(val2,val[u][0][0]);
u = father[u][0];
if(val[v][0][0]>=val1){
val1 = val[v][0][0];
val2 = max(val2,val[v][0][1]);
}else val2 = max(val2,val[v][0][0]);
v = father[v][0];
}
void work(){
sort(E+1,E+m+1,cmp);
for(int i = 1;i <= n; ++ i) fa[i] = i;
for(int i = 1;i <= m; ++ i) {
int u = E[i].u,v = E[i].v;
int fx = find(u),fy = find(v);
if(fx != fy){sum += E[i].w;vis[i]=1;fa[fx] = fy;eadd(u,v,E[i].w);eadd(v,u,E[i].w);}
}
dep[1] = 1;dfs(1,0);ans = 1e17;
for(int i = 1;i <= m; ++ i){
if(vis[i]) continue;
get_max(E[i].u,E[i].v);
if(val1 == E[i].w) ans = min(ans,sum-val2+E[i].w);
else ans = min(ans,sum-val1+E[i].w);//当最大值等于要加的边,就用次大值
}
put(ans);
}
int main(){
// file();
readdata();
work();
return 0;
}
最短路径生成树
/**********************
User:Mandy
Language:c++
Problem:luogu
Algorithm:
**********************/
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1005;
const int maxm = 1000005;
const long long mod = (1ll<<31)-1;
int n,m,dis[maxn];
int size,first[maxn];
long long cnt[maxn];
bool vis[maxn];
struct Edge{
int v,nt,w;
}edge[maxm];
struct Node{
int u,dis;
Node(int _u=0,int _dis=0):u(_u),dis(_dis){}
bool operator <(const Node &a)const{return dis>a.dis;}
}d[maxn];
template<class T>inline void read(T &x){
x = 0;char ch = getchar();bool flag = 0;
while(!isdigit(ch)) flag |= ch == '-',ch = getchar();
while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();
if(flag) x = -x;
}
template<class T>void putch(const T x){
if(x > 9) putch(x / 10);
putchar(x % 10 | 48);
}
template<class T>void put(const T x){
if(x < 0) putchar('-'),putch(-x);
else putch(x);
}
void file(){
freopen("349.in","r",stdin);
freopen("349.out","w",stdout);
}
void eadd(int u,int v,int w){
edge[++size].v = v;
edge[size].w = w;
edge[size].nt = first[u];
first[u] = size;
}
void readdata(){
read(n);read(m);
for(int i = 1;i <= m; ++ i){
int u,v,w;read(u);read(v);read(w);
eadd(u,v,w);eadd(v,u,w);
}
}
void dijkstra(){
priority_queue<Node>q;
memset(dis,0x3f3f3f3f,sizeof(dis));
q.push(Node(1,0));dis[1] = 0;
while(!q.empty()){
int u = q.top().u;q.pop();
if(vis[u])continue;vis[u]=1;
for(int i = first[u];i;i = edge[i].nt){
int v = edge[i].v;
if(dis[u]+edge[i].w < dis[v]){
dis[v] = dis[u] + edge[i].w;
q.push(Node(v,dis[v]));
}
}
}
}
bool cmp(const Node &x,const Node &y){
return x.dis < y.dis;
}
void work(){
dijkstra();
for(int i = 1;i <= n; ++ i) d[i].u=i,d[i].dis=dis[i];
sort(d+1,d+n+1,cmp);
for(int i = 1;i <= n; ++ i){
int u = d[i].u;//取出节点
for(int j = first[u];j;j = edge[j].nt){//遍历边
int v = edge[j].v;
if(dis[v] == dis[u]+edge[j].w) cnt[v]++;
}
}
long long ans = 1;cnt[1]=1;//初始化
for(int i = 1;i <= n; ++ i) ans = ans*cnt[i]%mod;
put(ans);
}
int main(){
// file();
readdata();
work();
return 0;
}
最小度限制生成树
/*********************
User:Mandy
Language:c++
Problem:luogu
Algorithm:
*********************/
//这道题,明显矩阵好做一点
//断边的时候链表不好断
#include<bits/stdc++.h>
using namespace std;
const int maxn = 35;
int n,cnt,s,md,size,tot,ans = 0,fa[maxn],tmp[maxn],t;
int len[maxn],father[maxn];
char name[maxn<<1][maxn];
//bool vis[maxn];
int edge2[maxn][maxn];
struct Edge{
int u,v,w;
}edge[maxn*maxn],edge1[maxn];
void eadd(int u,int v,int w){
edge[++size].u = u;edge[size].v = v;edge[size].w = w;
}
bool same(int x,int y){
if(len[x] != len[y]) return 0;
for(int i = 0;i < len[x]; ++ i)
if(name[x][i]!=name[y][i]) return 0;
return 1;//return 1
}
void readdata(){
read(n);
for(int i = 1;i <= n; ++ i){
int u=-1,v=-1,w=-1;memset(name[cnt],0,sizeof(name[cnt]));
scanf("%s",name[cnt]);len[cnt] = strlen(name[cnt]);
for(int j = 0;j < cnt; ++ j) if(same(cnt,j)) {u = j;break;}
if(u==-1) u = cnt,cnt++;memset(name[cnt],0,sizeof(name[cnt]));
scanf("%s",name[cnt]);len[cnt] = strlen(name[cnt]);
for(int j = 0;j < cnt; ++ j) if(same(cnt,j)) {v = j;break;}
if(v==-1) v = cnt,cnt++;
read(w);eadd(u,v,w);
}
memset(name[cnt],0,sizeof(name[cnt]));//清空
name[cnt][0] = 'P'; name[cnt][1] = 'a';
name[cnt][2] = 'r'; name[cnt][3] = 'k';
len[cnt] = 4;//更改len值
for(int i = 0;i < cnt; ++ i)
if(same(cnt,i)){s = i;break;}
read(md);
}
void init(){
memset(name,0,sizeof(name));memset(fa,-1,sizeof(fa));//注意初始化
memset(father,0,sizeof(father));memset(tmp,0,sizeof(tmp));
memset(edge,0,sizeof(edge));memset(edge1,0,sizeof(edge1));
memset(edge2,0,sizeof(edge2));memset(len,0,sizeof(len));
n = 0,cnt = 0 ,s = 0,md = 0,size = 0,tot = 0,ans = 0;
}
bool cmp(const Edge &a,const Edge &b){
return a.w < b.w;
}
int find(int x){
return father[x] == x ? x : father[x] = find(father[x]);
}
void dfs(int u,int f){
if(fa[u] == f) return;//剪枝
fa[u] = f;
for(int i = 0;i < cnt;++ i){
if(i == f || i == u || !edge2[u][i]) continue;
tmp[i] = edge2[u][i];dfs(i,u);
//记录v与父亲的边的边权
}
}
void work(){
sort(edge+1,edge+n+1,cmp);size = 0;
//与s相连的放在edge1里,用结构体,保证小的在前
//防止后面会更新连着S的边,而连着S的这条边不是最大
for(int i = 0;i <= cnt; ++ i) father[i] = i;
for(int i = 1;i <= n; ++ i){
int u = edge[i].u,v = edge[i].v,w = edge[i].w;
if(u == s || v == s){edge1[++tot] = edge[i];continue;}
int fx = find(u),fy = find(v);
if(fx != fy) ans += edge[i].w,father[fx] = fy,edge2[u][v] = edge2[v][u] = w;
}
//如果连通块的数量大于md,则无解
//不过这题好像没有说无解怎么办
//那就先默认有解
while(md){
int num = 0,id=0,idu=0;
for(int i = 1;i <= tot; ++ i){
int u = edge1[i].u,v = edge1[i].v;
int fx = find(u),fy = find(v);
int x = u == s ? v:u;
if(fx != fy) {
father[fy] = fx;tmp[x] = edge1[i].w;
dfs(x,s);md--;ans+=tmp[x];
} else {//找最大的可断边
int f = x,cur = 0,xx;
while(f!=s){
if(cur < tmp[f]){cur = max(cur,tmp[f]);xx = f;}//记录要断的边
f = fa[f];
}
if(cur-edge1[i].w>num) {num = cur-edge1[i].w;id = i;idu = xx;}
//注意edge1
//idu是要断的边,id是要连的边(点)
}
}
if(fa[idu] == s) md++;//如果断点与起点相连
if(num > 0 && md){
md--;
ans -= num;
edge2[idu][fa[idu]]=0;
edge2[fa[idu]][idu] = 0;//断边
int xx = edge1[id].u == s ? edge1[id].v:edge1[id].u;
dfs(xx,s);//重新定义父子
tmp[xx] = edge1[id].w;//注意tmp[xx]
} else break;
}
printf("Total miles driven: %d\n",ans);//这个换行有毒
}
int main(){
// file();
read(t);
// t = 1;
while(t--){
init();
readdata();
work();
if(t) puts(""); //这个换行有毒
}
return 0;
}
最小生成树计数
#include<bits/stdc++.h>
using namespace std;
const int maxn = 105;
const int maxm = 1005;
const int mod = 31011;
int n,m,size,tot;
int father[maxn];
int cnt,ans,sum;
int son[maxn];
struct Node{
int l,r,num;
}a[maxm];
struct Edge{
int u,v,w;
}edge[maxm];
void eadd(int u,int v,int w){
edge[++size].v = v;
edge[size].u = u;
edge[size].w = w;
}
bool cmp(const Edge &a,const Edge &b){
return a.w < b.w;
}
void readdata(){
read(n);read(m);
for(int i = 1;i <= m; ++ i){
int u,v,w;
read(u);read(v);read(w);
eadd(u,v,w);
}
sort(edge + 1,edge + m + 1,cmp);
}
int find1(int x){//压缩路径
return father[x] == x ? x : father[x] = find1(father[x]);
}
int find2(int x){//不压缩路径
return father[x] == x ? x : find2(father[x]);
}
void merge(int x,int y){
father[find1(x)] = find1(y);
}
void merge1(int x,int y){
if(son[x] > son[y]){
father[y] = x;
son[x] += son[y];
son[y] = son[x];
} else {
father[x] = y;
son[y] += son[x];
son[x] = son[y];
}
}//按秩合并
void dfs(int id,int pos,int num){
//id - 边的长度的编号
//pos - 边的编号
//num - 这条边在最小生成树中的个数
if(pos == a[id].r + 1){//到边了
if(num == a[id].num) sum++;
return;//如果不等于在最小生成树中的个数,说明方案错了
}
int u = find2(edge[pos].u);
int v = find2(edge[pos].v);//不能压缩路径
if(u != v){
father[u] = v;
dfs(id,pos+1,num +1);//选这条边
father[u] = u;
}
dfs(id,pos + 1,num);//不选这条边
}
void work(){
edge[0].w = -10000;
edge[m+1].w = -10000;
for(int i = 1;i <= n; ++ i) father[i] = i;
for(int i = 1;i <= m; ++ i){
int u = find1(edge[i].u);
int v = find1(edge[i].v);
if(edge[i-1].w != edge[i].w){
a[cnt].r = i-1;
a[++cnt].l = i;//a数组中存下相同长度的边的左右边界
}
if(u != v){
a[cnt].num++;//存下这个长度的边在最小生成树中的个数
merge(u,v);
++tot;
}
}
a[cnt].r = m;//给最后一个加上右边界
if(tot != n-1) {
puts("0");
return;
}
for(int i = 1;i <= n; ++ i) father[i] = i;
for(int i = 1;i <= n; ++ i) son[i] = 1;
ans = 1;//初始化
for(int i = 1;i <= cnt; ++ i){
sum = 0;
dfs(i,a[i].l,0);
ans = ans * sum % mod;
for(int j = a[i].l;j <= a[i].r; ++ j){
int fx = find2(edge[j].u);
int fy = find2(edge[j].v);//按最小生成树连边
if(fx != fy) merge1(fx,fy);
}
}
printf("%d",ans);
}
int main(){
// file();
readdata();
work();
return 0;
}