子串
kmp模板,偷懒直接find()了 0.0
#include
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
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 模板
#include
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
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 ;
}
可达性
/*
求出若干个强连通分量后,进行缩点:
将每个强连通分量看做DAG图中的一个点,建立各点之间的边(本题直接统计各点(连通分量)入度)
入度为0的强连通分量点集中取最小的点 构成所求集合
*/
#include
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
#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
#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
#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
#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 ;
}
【注】:这个题目求任意两点路径中的最小值,最大值同理。
另外,这个思路可以作为求次小生成树的更优解法。常规做法见次小生成树 , 利用这题的思路时间上可以更优: 枚举不在最小生成树中的边
Travel
思路:点u到点v有两种方式:
一是不经过传送,只在环上逆时针或者顺时针走
二是会用到传送门(经过传送点x最短路径就是u到x最短路+x到v最短路)
两种情况取最小值就是答案
#include
#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
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
#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
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
#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 ;
}
Graph Coloring I
思路:
染色法判断有无简单奇环
有简单奇环则染色不成功
输出环那部分比较难受
#include
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
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 ;
}