kmp
子串
kmp模板,偷懶直接find()了 0.0
#include <bits/stdc++.h>
using namespace std;
int main() {
int n ;
string s , t ;
cin >> n >> t ;
vector<int>v ;
for( int k = 2 ; k <= 16 ; k++ ) {
s = "" ;
for( int i = 1 ; i <= n ; i++ ) {
int tmp = i ;
while( tmp ) {
v.push_back( tmp % k ) ;
tmp /= k ;
}
for( int i = v.size() - 1 ; i >= 0 ; i-- ) {
if( v[i] >= 10 ) {
s += (char)( v[i] - 10 + 'A' ) ;
} else {
s += (char)( v[i] + '0' ) ;
}
}
v.clear() ;
}
int pos = s.find(t) ;
if( pos >= 0 && pos < s.size() ) {
cout << "yes" << endl ;
return 0 ;
}
}
cout << "no" << endl;
return 0 ;
}
慄醬的數列
思路:kmp+差分
滿足( a1 + b1 ) % k == ( a2 + b2 ) % k , 與滿足 ( a2 - a1 == b1 - b2 ) 一樣
轉換之後套kmp,找總共幾個串滿足
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int AX = 2e5 + 666 ;
LL a[AX] ;
LL b[AX] ;
int nxt[AX] ;
int res , n , m ;
void get_next(){
nxt[0] = -1 ;
int j = 0 , k = -1 ;
while( j < m ){
if( k == -1 || b[k] == b[j] ){
nxt[++j] = ++k ;
}else{
k = nxt[k] ;
}
}
}
void kmp(){
int i = 0 , j = 0 ;
while( i < n && j < m ){
if( j == -1 || a[i] == b[j] ){
i ++ ;
j ++ ;
}else j = nxt[j] ;
if( j == m ){ //
res ++ ;
j = nxt[j] ;
}
}
}
int main(){
int T ;
LL k ;
cin >> T ;
while( T-- ){
res = 0 ;
memset( nxt , 0 ,sizeof(nxt) ) ;
cin >> n >> m >> k ;
for( int i = 0 ; i < n ; i++ ){
cin >> a[i] ;
}
for( int i = 0 ; i < m ; i++ ){
cin >> b[i] ;
}
for( int i = 1 ; i < n ; i++ ){
a[i-1] = ( ( a[i-1] - a[i] ) % k + k ) % k ;
}
for( int i = 1 ; i < m ; i++ ){
b[i-1] = ( ( b[i] - b[i-1] ) % k + k ) % k ;
}
m --;
n --;
get_next();
kmp();
cout << res << endl ;
}
return 0 ;
}
manacher
迴文串
Manacher 模板
#include <bits/stdc++.h>
using namespace std;
const int AX = 3e3 + 666 ;
char s[AX] ;
int p[AX] ;
int main(){
while( ~scanf("%s",s) ){
int len = strlen(s) ;
for( int i = len ; i >= 0 ; i-- ){
s[2*i+1] = '#' ;
s[2*i+2] = s[i] ;
}
s[0] = '$' ;
int id = 0 , mx = 0 ;
len *= 2 ;
memset( p , 0 , sizeof(p) );
int res = 0 ;
for( int i = 2 ; i <= len ; i++ ){
if( p[id] + id > i ) p[i] = min( p[id] + id - i , p[2*id-i] );
else p[i] = 1 ;
while( s[i-p[i]] == s[i+p[i]] ) p[i]++ ;
if( p[i] + i > mx ){
mx = p[i] + i ;
id = i ;
}
res = max( res , p[i] );
}
printf("%d\n",res-1);
}
return 0 ;
}
最長迴文
思路:要求兩個串的子串拼起來爲迴文 的最大長度,首先分別對兩個串求最長迴文,枚舉迴文中心,然後向兩邊擴展,A的左邊 == B的右邊,就擴展。
#include <bits/stdc++.h>
using namespace std;
const int AX = 2e5 + 666 ;
char a[AX] , b[AX] ;
int pa[AX] , pb[AX] ;
int n ;
void manacher( char *s , int *p ){
for( int i = n ; i >= 0 ; i-- ){
s[2*i+2] = s[i] ;
s[2*i+1] = '#' ;
}
s[0] = '$';
int id = 0 , mx = 0 ;
for( int i = 2 ; i <= 2 * n ; i++ ){
if( p[id] + id > i ) p[i] = min( p[id] + id - i , p[2*id-i] ) ;
else p[i] = 1 ;
while( s[i+p[i]] == s[i-p[i]] ) p[i] ++ ;
if( i + p[i] > mx ){
mx = p[i] + i ;
id = i ;
}
}
}
int main() {
cin >> n ;
cin >> a >> b ;
manacher( a , pa );
manacher( b , pb );
int res = 0 ;
for( int i = 2 ; i <= 2 * n ; i++ ){
int tmp = max( pa[i] , pb[i-2] );
while( a[i-tmp] == b[i+tmp-2] ) tmp ++ ;
res = max( res , tmp );
}
cout << res - 1 << endl;
return 0 ;
}
Tarjan
/*
求出若干個強連通分量後,進行縮點:
將每個強連通分量看做DAG圖中的一個點,建立各點之間的邊(本題直接統計各點(連通分量)入度)
入度爲0的強連通分量點集中取最小的點 構成所求集合
*/
#include <bits/stdc++.h>
using namespace std ;
const int AX = 1e5 + 666 ;
struct Node {
int u , v , nxt ;
Node() {}
Node( int u , int v , int nxt ):u(u),v(v),nxt(nxt) {}
} G[AX];
vector<int>res ;
int head[AX] ;
int dfn[AX] ;
int low[AX] ;
int vis[AX] ;
int scc_id[AX] ;
int tot , cnt ;
int scc ;
int in[AX] ;
vector<int>id[AX] ; // 強連通分量集合
void add( int u , int v ) {
G[tot] = Node( u , v , head[u] ) ;
head[u] = tot ++ ;
}
stack<int>s;
void Tarjan( int u ) {
dfn[u] = low[u] = ++cnt ;
s.push(u);
vis[u] = 1 ;
for( int i = head[u] ; ~i ; i = G[i].nxt ) {
int v = G[i].v ;
if( !dfn[v] ) {
Tarjan( v ) ;
low[u] = min( low[u] , low[v] ) ;
}else if( vis[v] ){
low[u] = min( low[u] , low[v] ) ;
}
}
if( dfn[u] == low[u] ) {
int v = 0 ;
while( u != v ){
vis[s.top()] = 0 ;
v = s.top() ;
scc_id[v] = scc ;
id[scc].push_back(v);
s.pop() ;
}
scc ++ ;
}
}
int main() {
int n , m , x , y ;
scanf("%d%d",&n,&m);
cnt = tot = 0 ;
scc = 0 ;
memset( head , -1 , sizeof(head) ) ;
memset( dfn , 0 , sizeof(dfn) ) ;
memset( vis , 0 , sizeof(vis) ) ;
memset( in , 0 , sizeof(in) ) ;
for( int i = 0 ; i < m ; i++ ) {
scanf("%d%d",&x,&y);
add( x , y );
}
for( int i = 1 ; i <= n ; i++ ) {
if( !dfn[i] ) {
Tarjan( i ) ;
}
}
for( int i = 1 ; i <= n ; i++ ){
for( int j = head[i] ; ~j ; j = G[j].nxt ){
if( scc_id[G[j].v] != scc_id[i] ){
in[scc_id[G[j].v]]++ ; // 縮點
}
}
}
for( int i = 0 ; i < scc ; i++ ){
sort( id[i].begin() , id[i].end() );
}
for( int i = 0 ; i < scc ; i++ ){
if( !in[i] ) res.push_back(id[i][0]); // 入度0的最小點
}
sort( res.begin() , res.end() );
int len = res.size() ;
printf("%d\n",len);
for( int i = 0 ; i < len ; i++ ) {
printf("%d ",res[i]);
}
printf("\n");
return 0 ;
}
Blockade
思路:去掉割點會造成若干個集合之間的點不能連接,去掉普通的點只會造成1個點與其餘所有點不能連接。
故本題關鍵求割點對答案的貢獻,有三部分:
一是和普通點一樣,去掉的點與其餘各點( n-1 ) * 2
二是去掉割點後,割點的 每個 子連通塊與其餘節點的數對
三是去掉割點後,割點所有上部的節點與其餘節點的數對
#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int AX = 1e5 + 66 ;
int head[AX] ;
int dfn[AX] ;
int low[AX] ;
LL res[AX] ;
int num[AX] ;
int tot , cnt ;
int n , m ;
struct Node{
int v , nxt ;
Node(){}
Node( int v , int nxt ):v(v),nxt(nxt){}
}G[1000010] ;
void add( int u , int v ){
G[tot] = Node( v , head[u] ) ;
head[u] = tot ++ ;
G[tot] = Node( u , head[v] ) ;
head[v] = tot ++ ;
}
void Tarjan( int u , int pre ){
dfn[u] = low[u] = ++cnt ;
res[u] += 2LL * ( n - 1 ) ; // 1. 被去掉的點到其他各點
num[u] = 1 ;
LL sum = 0 ;
for( int i = head[u] ; ~i ; i = G[i].nxt ){
int v = G[i].v ;
if( !dfn[v] ){
Tarjan( v , u ) ;
num[u] += num[v] ; // u節點及子樹節點數目
low[u] = min( low[u] , low[v] ) ;
if( low[v] >= dfn[u] ){ // u爲割點
res[u] += 1LL * num[v] * ( n - num[v] - 1 ) ; // 2. 割點去除後的每個子連通塊同其他連通塊的數對
sum += num[v] ; // 割點子樹節點數目
}
}else if( v != pre ){
low[u] = min( low[u] , dfn[v] );
}
}
res[u] += 1LL * sum * ( n - sum - 1 ) ; // 3.sum爲割點子樹的所有點構成的連通塊節點數目(tarjan在求割點的過程中是一棵搜索樹往下遍歷)
}
int main() {
int u , v ;
memset( head , -1 , sizeof(head) ) ;
memset( res , 0 , sizeof(res) );
memset( low , 0 , sizeof(low) );
memset( dfn , 0 , sizeof(dfn) );
memset( num , 0 , sizeof(num) );
tot = cnt = 0 ;
scanf("%d%d",&n,&m);
for( int i = 0 ; i < m ; i++ ) {
scanf("%d%d",&u,&v);
add( u , v ) ;
}
Tarjan( 1 , 0 ) ;
for( int i = 1 ; i <= n ; i++ ){
printf("%lld\n",res[i]);
}
return 0 ;
}
最小生成樹
1.道路建設
思路:模板
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int AX = 2e2 + 66 ;
int a[AX][AX];
int dis[AX] ;
int vis[AX] ;
int c , n , m , x , y , w ;
int prim() {
int res = 0 ;
memset( dis , 0x3f , sizeof(dis) ) ;
memset( vis , 0 , sizeof(vis) ) ;
dis[1] = 0 ;
for( int i = 1 ; i <= n ; i++ ) {
int minu = INF , id = 1 ;
for( int j = 1 ; j <= n ; j++ ) {
if( !vis[j] && minu > dis[j] ) {
id = j ;
minu = dis[j] ;
}
}
if( minu == INF ) return -1 ;
res += minu ;
vis[id] = 1 ;
for( int j = 1 ; j <= n ; j++ ) {
if( !vis[j] && dis[j] > a[id][j] ) {
dis[j] = a[id][j] ;
}
}
}
return res ;
}
int main() {
while( ~scanf("%d%d%d",&c,&m,&n) ) {
memset( a , 0x3f , sizeof(a) ) ;
for( int i = 0 ; i < m ; i++ ) {
scanf("%d%d%d",&x,&y,&w);
a[x][y] = min( a[x][y] , w ) ;
a[y][x] = a[x][y] ;
}
int res = prim();
//cout << res << endl;
if( res != -1 && res <= c ) printf("Yes\n");
else printf("No\n");
}
return 0 ;
}
2.[HAOI2006]聰明的猴子
思路:找最小生成樹的最大邊。
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define eps 1e-10
using namespace std ;
const int AX = 1e3 + 66 ;
struct Node {
double x , y ;
} G[AX] ;
int n , m ;
double get_dis( Node a , Node b ) {
return sqrt( ( a.x - b.x ) * ( a.x - b.x ) + ( a.y - b.y ) * ( a.y - b.y ) ) ;
}
double a[AX] ;
double dis[AX] ;
int vis[AX] ;
double ans ;
void prim() {
for( int i = 0 ; i < n ; i++ ){
dis[i] = INF ;
}
memset( vis , 0 , sizeof(vis) ) ;
dis[0] = 0 ;
for( int i = 1 ; i <= n ; i++ ) {
double minu = INF ;
int id = 0 ;
for( int j = 0 ; j < n ; j++ ) {
if( !vis[j] && minu > dis[j] ) {
minu = dis[j] ;
id = j ;
}
}
if( minu == INF ) return ;
if( minu > ans ) {
ans = minu ;
}
vis[id] = 1 ;
for( int j = 0 ; j < n ; j++ ) {
double tmp = get_dis( G[id] , G[j] ) ;
if( !vis[j] && dis[j] > tmp ) {
dis[j] = tmp ;
}
}
}
}
int main() {
scanf("%d",&m);
for( int i = 0 ; i < m ; i++ ) {
scanf("%lf",&a[i]) ;
}
scanf("%d",&n);
for( int i = 0 ; i < n ; i++ ) {
scanf("%lf%lf",&G[i].x,&G[i].y);
}
ans = 0 ;
prim();
int res = 0 ;
for( int i = 0 ; i < m ; i++ ){
if( a[i] >= ans ) res ++ ;
}
printf("%d\n",res);
return 0 ;
}
貨車運輸
思路:最大生成樹+LCA
先求出最大生成樹,再通過Tarjan算法,利用minu[x]記錄點x到其 目前集合根節點 路徑中 邊的最小值
每次訪問完以u爲根的樹後,處理查詢(x,y),且處理的查詢滿足 x , y 的LCA都爲u
其路徑中最短邊長度是 合併集合後 的 min(minu[x],minu[y])
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef pair<int,int>P;
const int AX = 5e4 + 66 ;
int n , m ;
int tot ;
struct Edge {
int u , v , w ;
Edge( int u , int v , int w ):u(u),v(v),w(w) {}
bool operator < ( const Edge &a )const {
return w > a.w ;
}
};
struct Node {
int u , v , w , nxt ;
Node() {}
Node( int u , int v , int w , int nxt ):u(u),v(v),w(w),nxt(nxt) {}
} G[AX] ;
struct RES{
int x , y , id ;
RES( int x , int y , int id ):x(x),y(y),id(id){}
};
vector<RES>t_u[AX] ;
vector<Edge>e;
vector<P>que[AX] ;
int res[AX] ;
int head[AX] ;
int vis[AX] ;
int pre[AX] ;
int minu[AX] ;
void add( int u , int v , int w ){
G[tot] = Node( u , v , w , head[u] ) ; head[u] = tot ++ ;
G[tot] = Node( v , u , w , head[v] ) ; head[v] = tot ++ ;
}
int find( int x ){
//return x == pre[x] ? x : pre[x] = find(pre[x]) ;
int tmp = pre[x] ;
if( x != tmp ){
pre[x] = find(pre[x]);
}
minu[x] = min( minu[x] , minu[tmp] );
return pre[x] ;
}
void mix( int x , int y ){
x = find(x) ;
y = find(y) ;
if( x != y ){
pre[x] = y ;
}
}
void Tarjan( int u ){
vis[u] = 1 ;
for( int i = head[u] ; ~i ; i = G[i].nxt ){
int v = G[i].v ;
if( vis[v] ) continue ;
Tarjan(v) ;
minu[v] = G[i].w ;
pre[v] = u ;
}
for( int i = 0 ; i < (int)que[u].size() ; i++ ){ // 訪問完以u爲根的所有子樹後,處理與u有關的詢問
int v = que[u][i].first ;
int id = que[u][i].second ;
if( !vis[v] ) continue ;
t_u[find(v)].push_back(RES(u,v,id)); // 與u相關的查詢(u,v),加入到find(v)集合去處理
}
for( int i = 0 ; i < t_u[u].size() ; i++ ){ // 處理所有LCA爲u的查詢(x,y),其路徑中最短邊長度是合併集合後的min(minu[x],minu[y])
int id = t_u[u][i].id ;
int x = t_u[u][i].x ;
int y = t_u[u][i].y ;
find(x) ; find(y) ;
res[id] = min( minu[x] , minu[y] );
}
}
int main() {
int x , y , w , q ;
tot = 0 ;
scanf("%d%d",&n,&m);
for( int i = 1 ; i <= n ; i++ ){
pre[i] = i ;
}
memset( head , -1 , sizeof(head) );
memset( res , -1 , sizeof(res) ) ;
memset( vis , 0 , sizeof(vis) ) ;
while( m-- ) {
scanf("%d%d%d",&x,&y,&w);
e.push_back(Edge(x,y,w));
e.push_back(Edge(y,x,w));
}
scanf("%d",&q);
for( int i = 0 ; i < q ; i++ ) {
scanf("%d%d",&x,&y);
que[x].push_back(P(y,i));
que[y].push_back(P(x,i));
}
sort( e.begin() , e.end() ) ;
for( int i = 0 ; i < (int)e.size() ; i++ ){
int u = e[i].u ;
int v = e[i].v ;
if( find(u) != find(v) ){
mix( u ,v ) ;
add( u , v , e[i].w );
}
}
for( int i = 1 ; i <= n ; i++ ){
pre[i] = i ;
}
memset( minu , 0x3f , sizeof(minu) );
Tarjan(1);
for( int i = 0 ; i < q ; i++ ){
printf("%d\n",res[i]);
}
return 0 ;
}
【注】:這個題目求任意兩點路徑中的最小值,最大值同理。
另外,這個思路可以作爲求次小生成樹的更優解法。常規做法見次小生成樹 , 利用這題的思路時間上可以更優: 枚舉不在最小生成樹中的邊<x,y>,查詢生成樹中x到y路徑中的最大邊長度,去除此最大長度,加上邊<x,y>,取最小值
最短路
Travel
思路:點u到點v有兩種方式:
一是不經過傳送,只在環上逆時針或者順時針走
二是會用到傳送門(經過傳送點x最短路徑就是u到x最短路+x到v最短路)
兩種情況取最小值就是答案
#include <bits/stdc++.h>
#define LL long long
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
typedef pair<LL,int>P;
const int AX = 6e4 + 666 ;
int tot ;
int head[AX] ;
LL dis[50][AX] ;
LL pre[AX];
struct Node {
int u , v , nxt ;
LL w ;
Node() {}
Node( int u , int v , LL w , int nxt ):u(u),v(v),w(w),nxt(nxt) {}
} G[AX<<1];
int vis[AX] ;
void add( int u , int v , LL w ) {
G[tot] = Node( u , v , w , head[u] ) ;
head[u] = tot++ ;
G[tot] = Node( v , u , w , head[v] ) ;
head[v] = tot++ ;
}
void dijstra( int id ) {
memset( vis , 0 ,sizeof(vis) ) ;
int s = t[id] ;
dis[id][s] = 0 ;
priority_queue<P,vector<P>, greater<P> >q ;
q.push(P(0,s)) ;
while( !q.empty() ) {
P tmp = q.top() ;
q.pop() ;
int u = tmp.second ;
if( vis[u] ) continue ;
vis[u] = 1 ;
for( int i = head[u] ; ~i ; i = G[i].nxt ) {
int v = G[i].v ;
if( !vis[v] && dis[id][v] > dis[id][u] + G[i].w ) {
dis[id][v] = dis[id][u] + G[i].w ;
q.push(P(dis[id][v],v));
}
}
}
}
int main() {
int n , m , x , y ,q ;
LL w ;
scanf("%d%d",&n,&m);
tot = 0 ;
memset( head , -1 , sizeof(head) ) ;
memset( dis , 0x3f , sizeof(dis) ) ;
pre[0] = 0 ;
for( int i = 1 ; i <= n ; i++ ) {
scanf("%d",&x) ;
add( i , ( i % n ) + 1 , x ) ;
pre[i] = pre[i-1] + x ;
}
map<int,int>mp;
while( m-- ) {
scanf("%d%d%lld",&x,&y,&w) ;
if( x == y ) continue ;
add( x , y , w ) ;
if( !mp[x] ) t.push_back(x);
if( !mp[y] ) t.push_back(y);
mp[x] = 1 ;
mp[y] = 1 ;
}
int len = t.size() ;
for( int i = 0 ; i < len ; i++ ) { // 求出帶傳送的點到其餘各點距離
dijstra( i ) ;
}
scanf("%d",&q);
while( q-- ) {
scanf("%d%d",&x,&y);
LL res = min( abs(pre[y-1] - pre[x-1]), pre[n] - abs(pre[y-1] - pre[x-1]) ) ; // 不經過傳送,順逆時針
for( int i = 0 ; i < len ; i++ ) {
res = min( res , dis[i][x] + dis[i][y] ) ; // 經過傳送
}
printf("%lld\n",res);
}
return 0 ;
}
最短路
思路:有負權無負環,用SPFA
#include <bits/stdc++.h>
using namespace std;
const int AX = 2e4 + 666 ;
int n , m ;
struct Node{
int to , w ;
Node( int to , int w ):to(to),w(w){}
};
vector<Node>G[AX] ;
int vis[AX] ;
int dis[AX] ;
void spfa(){
queue<int>q;
q.push(1);
vis[1] = 1 ;
memset( dis , 0x3f , sizeof(dis) ) ;
dis[1] = 0 ;
while( !q.empty() ){
int u = q.front() ;
q.pop() ;
vis[u] = 0 ;
for( int i = 0 ; i < G[u].size() ; i++ ){
int v = G[u][i].to ;
if( dis[v] > dis[u] + G[u][i].w ){
dis[v] = dis[u] + G[u][i].w ;
if( !vis[v] ){
vis[v] = 1 ;
q.push(v);
}
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
int x , y , w ;
while( m-- ){
scanf("%d%d%d",&x,&y,&w);
G[x].push_back(Node(y,w));
}
memset( vis , 0 , sizeof(vis) ) ;
spfa();
for( int i = 2 ; i <= n ; i++ ){
printf("%d\n",dis[i]);
}
return 0 ;
}
旅行
思路:枚舉中間點,求到各點距離,取兩段長度和的最大值。
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef pair<int,int>P;
const int AX = 1e3 + 66;
struct Node {
int v , w , nxt ;
Node() {}
Node( int v , int w , int nxt ):v(v),w(w),nxt(nxt) {}
} G[AX<<1] ;
int head[AX] ;
int vis[AX] ;
int tot ;
int dis[AX] ;
vector<int>ans ;
void add( int u , int v , int w ) {
G[tot] = Node( v , w , head[u] ) ;
head[u] = tot ++ ;
G[tot] = Node( u , w , head[v] ) ;
head[v] = tot ++ ;
}
bool cmp( int a , int b ) {
return a > b ;
}
int n , m , x , y , w ;
int dijstra( int s ) {
priority_queue< P , vector<P> , greater<P> >q ;
q.push(P(0,s)) ;
memset( vis , 0 , sizeof(vis) ) ;
memset( dis , 0x3f , sizeof(dis) ) ;
ans.clear() ;
dis[s] = 0 ;
while( !q.empty() ) {
P tmp = q.top() ;
q.pop() ;
int u = tmp.second ;
vis[u] = 1 ;
for( int i = head[u] ; ~i ; i = G[i].nxt ) {
int v = G[i].v ;
if( !vis[v] && dis[v] > dis[u] + G[i].w ) {
dis[v] = dis[u] + G[i].w ;
q.push(P(dis[v],v));
}
}
}
for( int j = 1 ; j <= n ; j++ ) {
if( j != s && dis[j] < INF )
ans.push_back( dis[j] ) ;
}
if( ans.size() < 2 ) return -1 ;
sort( ans.begin() , ans.end() , cmp ) ;
return ans[0] + ans[1] ;
}
int main() {
int T ;
scanf("%d",&T);
while( T-- ) {
tot = 0 ;
int res = -1 ;
memset( head , -1 , sizeof(head) ) ;
memset( dis , 0x3f , sizeof(dis) ) ;
scanf("%d%d",&n,&m);
while( m-- ) {
scanf("%d%d%d",&x,&y,&w);
add( x , y , w ) ;
}
for( int i = 1 ; i <= n ; i++ ) {
res = max( res , dijstra( i ) ) ;
}
printf("%d\n",res);
}
return 0 ;
}
最短路
思路:i到j路徑長度是 i ^ j ,這樣邊很多,可以將每條路徑分解成2的冪次長度相加,故每個點相連路徑只添加2的冪次長度,只有nlogn條邊。
#include <bits/stdc++.h>
using namespace std;
int n , m , c , s , t ;
const int AX = 2e5 + 66 ;
struct Node{
int v , w ;
Node( int v , int w ):v(v),w(w){}
bool operator < ( const Node &a )const{
return w > a.w ;
}
};
vector<Node>G[AX] ;
int dis[AX] ;
int vis[AX] ;
void dijstra(){
priority_queue<Node>q ;
q.push(Node(s,0));
memset( dis , 0x3f , sizeof(dis) ) ;
dis[s] = 0 ;
while( !q.empty() ){
Node tmp = q.top() ;
q.pop();
vis[tmp.v] = 1 ;
for( int i = 0 ; i < G[tmp.v].size() ; i++ ){
int to = G[tmp.v][i].v ;
if( !vis[to] && dis[to] > dis[tmp.v] + G[tmp.v][i].w ){
dis[to] = dis[tmp.v] + G[tmp.v][i].w ;
q.push(Node(to,dis[to]));
}
}
}
}
int main() {
scanf("%d%d%d",&n,&m,&c);
int x , y , w ;
for( int i = 0 ; i < m ; i++ ) {
scanf("%d%d%d",&x,&y,&w) ;
G[x].push_back(Node(y,w));
}
scanf("%d%d",&s,&t);
int tot = 1 ;
while( tot <= n ) tot <<= 1 ;
for( int i = 1 ; i < tot ; i++ ){
for( int p = 1 ; p < tot ; p <<= 1 ){
G[i].push_back(Node(i^p,p*c));
}
}
dijstra();
printf("%d\n",dis[t]);
return 0 ;
}
強連通分量:
這題跟求強連通似乎並無關係0.0
不過簡單題練練手
B題
思路:因爲是個環,解一定是從1順時針,或者逆時針。
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int AX = 1e2 + 66 ;
int n ;
int a[AX][AX];
int res ;
void dfs( int x , int pre , int num , int sum ){
if( num == n + 1 ){
res = min( res , sum );
return ;
}
for( int i = 1 ; i <= n ; i++ ){
if( a[x][i] >= 0 && i != pre ){
dfs( i , x , num + 1 , sum + a[x][i] );
}
}
}
int main() {
int x , y , w ;
while( ~scanf("%d",&n) ){
memset( a , -1 , sizeof(a) ) ;
res = INF ;
for( int i = 0 ; i < n ; i++ ){
scanf("%d%d%d",&x,&y,&w);
a[y][x] = w ;
a[x][y] = 0 ;
}
dfs( 1 , -1 , 1 , 0 );
printf("%d\n",res);
}
return 0 ;
}
二分圖匹配
思路:
染色法判斷有無簡單奇環
有簡單奇環則染色不成功
輸出環那部分比較難受
#include <bits/stdc++.h>
using namespace std;
const int AX = 3e5 + 66 ;
int n , m ;
vector<int>odd;
int col[AX] ;
vector<int>G[AX];
int pre[AX] ;
int f , s ;
void dfs( int x , int val ) {
if( f ) return ;
col[x] = val ;
for( int i = 0 ; i < (int)G[x].size() ; i ++ ) {
int y = G[x][i] ;
if( col[y] == -1 ) {
pre[y] = x ;
dfs( y , val ^ 1 );
} else if( col[x] == col[y] ) { // 有奇數環
f = y ; // 環終點
s = x ; // 環起點
return ;
}
}
return ;
}
int main() {
int x , y ;
scanf("%d%d",&n,&m);
f = 0 ; // 無奇環
memset( col , -1 , sizeof(col) ) ;
for( int i = 0 ; i < m ; i++ ) {
scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}
dfs( 1 , 0 ) ;
if( !f ) {
printf("0\n");
for( int i = 1 ; i <= n ; i++ ) {
printf("%d ",col[i]);
}
printf("\n");
} else {
int k = 0 ;
for( int i = f ; i != s ; i = pre[i] ){
k ++ ;
}
printf("%d\n", k + 1 );
for( int i = f ; i != s ; i = pre[i] ){
printf("%d ",i);
}
printf("%d\n",s);
}
return 0 ;
}
字典樹
單詞查找樹
比板子題還要簡單些的一道,以前複試題也沒出過,就不寫了
#include <bits/stdc++.h>
using namespace std ;
struct Trie{
struct Trie *nxt[26] ;
char c ;
};
Trie* create(){
Trie* rt = new Trie() ;
memset( rt -> nxt , 0 , sizeof(rt->nxt) ) ;
return rt ;
}
void insert( Trie* rt , char *s ){
int len = strlen(s) ;
Trie* t = rt ;
while( *s ){
int id = (*s)-'A' ;
if( t -> nxt[id] == NULL ){
t -> nxt[id] = create();
}
t = t -> nxt[id] ;
s ++ ;
}
}
int res ;
void dfs( Trie* rt ){
res ++ ;
for( int i = 0 ; i < 26 ; i++ ){
if( rt -> nxt[i] ){
dfs( rt -> nxt[i] );
}
}
}
int main(){
char s[66];
res = 0 ;
Trie* rt = create() ;
while( ~scanf("%s",s) ){
insert( rt , s ) ;
}
dfs( rt );
printf("%d\n",res);
return 0 ;
}