距离noip还有⑨-1天,差不多要开始撸模板了,在这里整理下noip各式各样算法的模板。
spfa:图上乱搞必备,并非只止步于求最短路 | 最长路,spfa可是图上dp!!
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int sz = 200100;
deque < int > q; // slf
int n,m;
int head[sz],nxt[sz],dist[sz];
struct gtnd
{
int t,d;
}l[sz];
int tot = 1;
bool use[sz];
int pre[sz];//记录路径
int tim[sz];//判负环
void build(int f,int t,int d)
{
l[tot].t = t;
l[tot].d = d;
nxt[tot] = head[f];
head[f] = tot ++;
}
int spfa(int s,int e)
{
for(int i = 1 ; i <= n ; i ++)
dist[i] = 2147483641;
dist[s] = 0;
use[s] = 1;
q.push_front(s);
while(!q.empty())
{
int f = q.front();
q.pop_front();
use[f] = 0;
for(int i = head[f] ; i ; i = nxt[i])
{
int t = l[i].t;
if(dist[t] > dist[f] + l[i].d)
{
dist[t] = dist[f] + l[i].d;
tim[t] = tim[f] + 1;
if(tim[t] > n)
return -1;
pre[t] = f;
if(!use[t])
{
use[t] = 1;
if(!q.empty())
{
if(dist[t] < dist[q.front()])
q.push_front(t);
else
q.push_back(t);
}
else
q.push_back(t);
}
}
}
}
return dist[e];
}
void print_path(int u) // 打印路径
{
printf("%d ",u);
if(pre[u])
print_path(pre[u]);
}
int main()
{
return 0;
}
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int sz = 200010;
int f[sz];
int n,m;
int find(int x)
{
if(f[x] == x)
return x;
return f[x] = find(f[x]);
}
struct gtnd
{
int f,t,d;
}l[sz];
bool cmp(gtnd swc,gtnd zcw)
{
return swc.d > zcw.d;
}
int Kruskal()
{
int ans = 0;
for(int i = 1 ; i <= n ; i ++)
f[i] = i;
sort(l+1,l+m+1,cmp);
for(int i = 1 ; i <= m ; i ++)
{
int u = l[i].f , v = l[i].t;
int fu = find(u) , fv = find(v);
if(fu != fv)
{
f[fu] = fv;
ans += l[i].d;
}
}
return ans;
}
int main()
{
return 0;
}
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int sz = 200010;
int head[sz],nxt[sz],l[sz];
int ru[sz];
int tot = 1;
int n,m;
queue < int > q;
void build(int f,int t)
{
l[tot] = t;
nxt[tot] = head[f];
head[f] = tot ++;
}
void top_sort()
{
for(int i = 1 ; i <= n ; i ++)
if(!ru[i])
{
q.push(i);
printf("%d ",i);
}
while(!q.empty())
{
int f = q.front();
q.pop();
for(int i = head[f] ; i ; i = nxt[i])
{
ru[l[i]] --;
if(!ru[l[i]])
{
printf("%d ",l[i]);
q.push(l[i]);
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1 ; i <= m ; i ++)
{
int f,t;
scanf("%d%d",&f,&t);
ru[t] ++;
build(f,t);
}
top_sort();
return 0;
}
树上最近公共祖先,lca,这里推荐倍增版,滋磁在树上快速搞事。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int size = 200010;
int head[size],next[size],dist[size][32];
int par[size][32],deep[size];
int tot = 1;
struct dc
{
int t,d;
}l[size];
void build(int f,int t,int d)
{
l[tot].t = t;
l[tot].d = d;
next[tot] = head[f];
head[f] = tot ++;
}
int n;
void dfs(int u,int fa,int dep,int dis)
{
par[u][0] = fa;
dist[u][0] = dis;
deep[u] = dep;
for(int i = head[u] ; i ; i = next[i])
{
int v = l[i].t;
if(v != fa)
dfs(v,u,dep+1,l[i].d);
}
}
int lca(int u,int v)
{
int ans = 0;
if(deep[u] < deep[v])
swap(u,v);
for(int i = 31 ; i >= 0 ; i --)
if(deep[par[u][i]] >= deep[v])
ans += dist[u][i] , u = par[u][i];
for(int i = 31 ; i >= 0 ; i --)
if(par[u][i] != par[v][i])
ans += dist[u][i] + dist[v][i] , u = par[u][i] , v = par[v][i];
if(u != v)
ans += dist[u][0] + dist[v][0] , u = par[u][0] , v = par[v][0];
return ans;
}
int main()
{
scanf("%d",&n);
for(int i = 1 ; i < n ; i ++)
{
int f,t,d;
scanf("%d%d%d",&f,&t,&d);
f ++ , t ++;
build(f,t,d);
build(t,f,d);
}
dfs(1,0,1,0);
for(int i = 1 ; i <= 31 ; i ++)
for(int j = 1 ; j <= n ; j ++)
par[j][i] = par[par[j][i-1]][i-1] , dist[j][i] = dist[j][i-1] + dist[par[j][i-1]][i-1];
int m;
scanf("%d",&m);
for(int i = 1 ; i <= m ; i ++)
{
int u,v;
scanf("%d%d",&u,&v);
printf("%d\n",lca(u+1,v+1));
}
return 0;
}
tarjan系列
tarjan 求 scc , 有向图大腿。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
#include<algorithm>
using namespace std;
const int sz = 200010;
int head[sz],nxt[sz],dist[sz],l[sz];
int low[sz],dfn[sz],scc_num,dfs_clock;
stack < int > s;
struct gtnd
{
int p,num;
bool operator <(const gtnd &a)const
{
return num < a.num;
}
}scc[sz];
int tarjan(int u)
{
dfn[u] = low[u] = dfs_clock ++;
s.push(u);
for(int i = head[u] ; i ; i = nxt[i])
{
int v = l[i];
if(!dfn[v])
{
low[v] = tarjan(v);
low[u] = min(low[u],low[v]);
}
else if(!scc[v].num)
low[u] = min(low[u],dfn[v]);
}
if(low[u] == dfn[u])
{
scc_num ++;
while(12 < 450)
{
if(s.empty())
break;
int v = s.top();
s.pop();
scc[v].num = scc_num;
scc[v].p = v;
if(u == v)
break;
}
}
return low[u];
}
int main()
{
return 0;
}
tarjan求割点,然而基本没用过,忘得差不多了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
#include<algorithm>
using namespace std;
const int sz = 200010;
int head[sz],nxt[sz],l[sz];
int tot = 1;
int n,m;
void build(int f,int t)
{
l[tot] = t;
nxt[tot] = head[f];
head[f] = tot ++;
}
int low[sz],dfn[sz],dfs_clock;
bool is_cut[sz];
int tarjan(int u,int fa)
{
dfn[u] = low[u] = ++ dfs_clock;
int child = 0;
for(int i = head[u] ; i ; i = nxt[i])
{
int v = l[i];
if(!dfn[v])
{
child ++;
low[v] = tarjan(v,u);
low[u] = min(low[u],low[v]);
if(dfn[u] <= low[v])
is_cut[u] = 1;
}
else if(dfn[v] < dfn[u] && v != fa)
low[u] = min(dfn[v],low[u]);
}
if(child == 1 && fa == 0)
is_cut[u] = 0;
return low[u];
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1 ; i <= m ; i ++)
{
int f,t;
scanf("%d%d",&f,&t);
build(f,t);
build(t,f);
}
for(int i = 1 ; i <= n ; i ++)
if(!dfn[i])
tarjan(i,0);
int ans = 0;
for(int i = 1 ; i <= n ; i ++)
if(is_cut[i])
ans ++;
printf("%d\n",ans);
for(int i = 1 ; i <= n ; i ++)
if(is_cut[i])
printf("%d ",i);
return 0;
}
tarjan求桥,就是割点改个等于号。
int low[sz],dfn[sz],dfs_clock;
struct gtnd
{
int f,t;
}cut[sz];
int conut;
int tarjan(int u,int fa)
{
dfn[u] = low[u] = ++ dfs_clock;
int child = 0;
for(int i = head[u] ; i ; i = nxt[i])
{
int v = l[i];
if(!dfn[v])
{
child ++;
low[v] = tarjan(v,u);
low[u] = min(low[u],low[v]);
if(dfn[u] < low[v])
cut[++conut].f = u , cut[count].t = v;
}
else if(dfn[v] < dfn[u] && v != fa)
low[u] = min(dfn[v],low[u]);
}
return low[u];
}
树的直径,滋磁树上乱搞,这里以codevs1814为例。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n;
const int size = 200010;
int head[size],next[size],l[size];
int tot = 1;
void build(int f,int t)
{
l[tot] = t;
next[tot] = head[f];
head[f] = tot ++;
}
int pos,dist;
void dfs(int u,int p,int dis)
{
if(dis > dist)
dist = dis , pos = u;
for(int i = head[u] ; i ; i = next[i])
{
int v = l[i];
if(v != p)
dfs(v,u,dis+1);
}
}
int main()
{
scanf("%d",&n);
for(int i = 1 ; i <= n ; i ++)
{
int ll,rr;
scanf("%d%d",&ll,&rr);
if(ll)
build(ll,i) , build(i,ll);
if(rr)
build(rr,i) , build(i,rr);
}
dfs(1,-1,0);
dist = 0;
dfs(pos,-1,0);
printf("%d\n",dist);
return 0;
}
floyd,n^3最短路,带有比较特殊的性质,可以有各种变形,但往往难度超出noip范围。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int sz = 2550;
int dis[sz][sz];
int n,m,s,e;
int main()
{
scanf("%d%d%d%d",&n,&m,&s,&e);
for(int i = 1 ; i <= n ; i ++)
for(int j = 1 ; j <= n ; j ++)
dis[i][j] = 214748364;
for(int i = 1 ; i <= n ; i ++)
dis[i][i] = 0;
for(int i = 1 ; i <= m ; i ++)
{
int f,t,d;
scanf("%d%d%d",&f,&t,&d);
dis[f][t] = min(dis[f][t],d);
dis[t][f] = min(dis[f][t],dis[t][f]);
}
for(int k = 1 ; k <= n ; k ++)
for(int i = 1 ; i <= n ; i ++)
for(int j = 1 ; j <= n ; j ++)
dis[i][j] = min(dis[i][j],dis[i][k] + dis[k][j]);
printf("%d\n",dis[s][e]);
return 0;
}
gcd & lcm ,noip唯有的几个可以加特技的算法。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int gcd(int a,int b)
{
if(b == 0)
return a;
return gcd(b,a%b);
}
int lcm(int a,int b)
{
return a * b / gcd(a,b);
}
int main()
{
int a,b;
while(scanf("%d%d",&a,&b))
{
printf("%d\n",gcd(a,b));
}
return 0;
}
埃氏筛,筛素数速度上仅次于欧拉筛。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
bool is_prime[2000100];
int main()
{
int n;
scanf("%d",&n);
is_prime[1] = 1;
for(int i = 2 ; i * i <= n ; i ++)
{
if(!is_prime[i])
for(int j = i * i ; j <= n ; j += i)
is_prime[j] = 1;
}
return 0;
}
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int ksm(int x,int p)
{
if(p == 0)
return 1;
if(p == 1)
return x;
if(p == 2)
return x * x;
int temp = ksm(x,p/2);
if(p % 2 == 1)
return temp * temp * x;
if(p % 2 == 0)
return temp * temp;
}
int main()
{
int x,p;
while(scanf("%d%d",&x,&p))
printf("%d\n",ksm(x,p));
return 0;
}
逆元,只推荐费马小定理,要是noip题mod不是素数我就暴力!
费马小定理: 假如p是质数,且a,p互质,那么 a^(p-1)≡1(mod p)。
由此可得,a^(p-2) ≡ 1 / a (mod p),所以在mod p意义下,除以 a 等价于乘上 a^(p-2),即 a^(p-2) 为 a 的逆元。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int mod = 1000000007;
ll ksm(ll x,ll p)
{
if(p == 0)
return 1;
if(p == 1)
return x % mod;
if(p == 2)
return ((x%mod) * (x%mod))%mod;
int temp = ksm(x,p/2) % mod;
if(p % 2 == 1)
return (((temp * temp) % mod) * (x%mod));
if(p % 2 == 0)
return (temp * temp) % mod;
}
int get(int a)
{
return ksm(a,mod-2);
}
int main()
{
return 0;
}
单调队列,滋磁O(n)序列上搞事,多用于dp优化。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int sz = 2000100;
deque < int > q;
int num[sz];
int n,k;
int main()
{
scanf("%d%d",&n,&k);
for(int i = 1 ; i <= n ; i ++)
scanf("%d",&num[i]);
for(int i = 1 ; i <= k ; i ++)
{
while(!q.empty() && num[q.back()] < num[i])
q.pop_back();
q.push_back(i);
}
printf("%d\n",num[q.front()]);
for(int i = k + 1 ; i <= n ; i ++)
{
while(!q.empty() && q.front() < i - k + 1)
q.pop_front();
while(!q.empty() && num[q.back()] < num[i])
q.pop_back();
q.push_back(i);
printf("%d\n",num[q.front()]);
}
return 0;
}
set,用于logn找前驱后继或当map使233,这里以noi openjudge的冷血格斗场为例。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
#define ll long long
using namespace std;
struct gtnd
{
ll id;
ll p;
bool operator <(const gtnd &a)const
{
return p < a.p;
}
};
map < ll , ll > m;
set < gtnd > s;
set < gtnd > :: iterator f1,f2,temp;
int n;
int main()
{
scanf("%d",&n);
gtnd sta;
sta.id = 1 , sta.p = 1000000000;
m[sta.p] = 1;
s.insert(sta);
for(int i = 1 ; i <= n ; i ++)
{
gtnd nxt;
scanf("%lld%lld",&nxt.id,&nxt.p);
printf("%lld ",nxt.id);
if(m[nxt.p])
{
printf("%lld\n",m[nxt.p]);
m[nxt.p] = min(m[nxt.p],nxt.id);
continue;
}
else
m[nxt.p] = nxt.id;
f1 = s.lower_bound(nxt);
f2 = f1;
f2 --;
ll id_f1 = m[(*f1).p] , id_f2 = m[(*f2).p];
ll a_f1 = abs((*f1).p - nxt.p) , a_f2 = abs((*f2).p - nxt.p);
if(f2 == s.end())
printf("%lld\n",id_f1);
else if(f1 == s.end())
printf("%lld\n",id_f2);
else if(a_f1 > a_f2)
printf("%lld\n",id_f2);
else if(a_f1 < a_f2)
printf("%lld\n",id_f1);
else
{
if(id_f1 < id_f2)
printf("%lld\n",id_f1);
else
printf("%lld\n",id_f2);
}
s.insert(nxt);
}
return 0;
}
线段树,noip考不到但是可以水分的大腿,滋磁区间快速搞事。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int sz = 200010;
struct xd_tree
{
int l,r;
ll sum,min,max,add;
}tree[sz*4];
int num[sz];
int n;
void updata(int p)
{
tree[p].min = min(tree[p<<1].min,tree[p<<1|1].min);
tree[p].max = max(tree[p<<1].max,tree[p<<1|1].max);
tree[p].sum = tree[p<<1].sum + tree[p<<1|1].sum;
}
void build_tree(int p,int l,int r)
{
tree[p].l = l , tree[p].r = r;
if(l == r)
{
tree[p].min = tree[p].max = tree[p].sum = num[l];
return ;
}
int mid = l + r >> 1;
build_tree(p<<1,l,mid);
build_tree(p<<1|1,mid+1,r);
updata(p);
}
void spread(int p)
{
if(tree[p].add)
{
tree[p<<1].sum += tree[p].add * (tree[p<<1].r - tree[p<<1].l + 1);
tree[p<<1|1].sum += tree[p].add * (tree[p<<1|1].r - tree[p<<1|1].l + 1);
tree[p<<1].min += tree[p].add;
tree[p<<1|1].min += tree[p].add;
tree[p<<1].max += tree[p].add;
tree[p<<1|1].max += tree[p].add;
tree[p<<1].add += tree[p].add;
tree[p<<1|1].add += tree[p].add;
tree[p].add = 0;
}
return ;
}
void change(int p,int l,int r,int x)
{
if(l <= tree[p].l && tree[p].r <= r)
{
tree[p].sum += x * (tree[p].r - tree[p].l + 1);
tree[p].max += x;
tree[p].min += x;
tree[p].add += x;
return ;
}
spread(p);
int mid = tree[p].l + tree[p].r >> 1;
if(l <= mid)
change(p<<1,l,r,x);
if(r > mid)
change(p<<1|1,l,r,x);
updata(p);
}
ll ask_sum(int p,int l,int r)
{
if(l <= tree[p].l && tree[p].r <= r)
return tree[p].sum;
spread(p);
int mid = tree[p].l + tree[p].r >> 1;
ll ans = 0;
if(l <= mid)
ans += ask_sum(p<<1,l,r);
if(r > mid)
ans += ask_sum(p<<1|1,l,r);
return ans;
}
ll ask_min(int p,int l,int r)
{
if(l <= tree[p].l && tree[p].r <= r)
return tree[p].min;
spread(p);
int mid = tree[p].l + tree[p].r >> 1;
ll ans = 214748364111111ll;
if(l <= mid)
ans = min(ask_min(p<<1,l,r),ans);
if(r > mid)
ans = min(ask_min(p<<1|1,l,r),ans);
return ans;
}
ll ask_max(int p,int l,int r)
{
if(l <= tree[p].l && tree[p].r <= r)
return tree[p].max;
spread(p);
int mid = tree[p].l + tree[p].r >> 1;
ll ans = 0;
if(l <= mid)
ans = max(ask_max(p<<1,l,r),ans);
if(r > mid)
ans = max(ask_max(p<<1|1,l,r),ans);
return ans;
}
int main()
{
scanf("%d",&n);
for(int i = 1 ; i <= n ; i ++)
scanf("%d",&num[i]);
build_tree(1,1,n);
return 0;
}
逆序对 && 归并排序
归并排序求逆序对是唯有的几个比较高效的求逆序对算法(线段树:???),前几年noip有用到。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int sz = 2000100;
ll ans;
int n;
int num[sz];
int temp[sz];
void merge_sort(int l,int r)
{
if(l == r)
return ;
int mid = l + r >> 1;
merge_sort(l,mid) , merge_sort(mid+1,r);
int p = l , pl = l , pr = mid + 1;
while(pl <= mid || pr <= r)
{
if(pr > r || (pl <= mid && num[pl] <= num[pr]))
temp[p ++] = num[pl ++];
else
temp[p ++] = num[pr ++] , ans += mid - pl + 1;
}
for(int i = l ; i <= r ; i ++)
num[i] = temp[i];
return ;
}
int main()
{
int n;
scanf("%d",&n);
for(int i = 1 ; i <= n ; i ++)
scanf("%d",&num[i]);
merge_sort(1,n);
printf("%lld\n",ans);
return 0;
}
状压搜索,noip还真没见过,模拟赛里倒是不少。
以HAOI2008移动玩具为例。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
bool use[2000100];
struct gtnd
{
int k;
int now;
};
queue < gtnd > q;
int st,ed;
void start_work()
{
for(int i = 0 ; i < 16 ; i ++)
{
char ins = getchar();
while(ins != '1' && ins != '0')
ins = getchar();
ins -= '0';
st |= ( ins << i );
}
for(int i = 0 ; i < 16 ; i ++)
{
char ins = getchar();
while(ins != '1' && ins != '0')
ins = getchar();
ins -= '0';
ed |= ( ins << i );
}
}
int bfs()
{
gtnd star;
star.k = st;
star.now = 0;
q.push(star);
use[st] = 1;
while(!q.empty())
{
gtnd f = q.front();
q.pop();
if(f.k == ed)
return f.now;
for(int i = 0 ; i < 16 ; i ++)
{
if((f.k >> i) & 1)
{
if(i > 3 && !((f.k >> (i-4)) & 1))
{
gtnd nxt;
nxt.now = f.now + 1;
nxt.k = f.k;
nxt.k ^= (1 << i-4);
nxt.k ^= (1 << i);
if(!use[nxt.k])
{
use[nxt.k] = 1;
q.push(nxt);
}
}
if(i < 12 && !((f.k >> (i+4)) & 1))
{
gtnd nxt;
nxt.now = f.now + 1;
nxt.k = f.k;
nxt.k ^= (1 << i+4);
nxt.k ^= (1 << i);
if(!use[nxt.k])
{
use[nxt.k] = 1;
q.push(nxt);
}
}
if(i % 4 != 0 && !((f.k >> (i-1)) & 1))
{
gtnd nxt;
nxt.now = f.now + 1;
nxt.k = f.k;
nxt.k ^= (1 << i-1);
nxt.k ^= (1 << i);
if(!use[nxt.k])
{
use[nxt.k] = 1;
q.push(nxt);
}
}
if(i % 4 != 3 && !((f.k >> (i+1)) & 1))
{
gtnd nxt;
nxt.now = f.now + 1;
nxt.k = f.k;
nxt.k ^= (1 << i+1);
nxt.k ^= (1 << i);
if(!use[nxt.k])
{
use[nxt.k] = 1;
q.push(nxt);
}
}
}
}
}
return -1;
}
int main()
{
start_work();
printf("%d\n",bfs());
return 0;
}
论如何正确地打开脑洞
自己的码力实现不了的做法还是少想;
看数据范围估计复杂度,看有没有套路,有没有可以套的模型,看特殊的条件等等。