快noip了就乱做一下历年的noip题目咯..
noip2014 飞扬的小鸟
其实这道题并不是很难,但是就有点难搞 听说男神错了一个小时..
就是$f_{i,j}$表示在第$i$个位置高度为$j$的时候最小点击次数
递推的话对于上升的情况只做一次,后面几次在后面再做..
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int Maxn = 10010;
const int Maxm = 1010;
const int inf = 1e9;
int f[2][Maxm];
int n, m, K;
int l[Maxn], u[Maxn]; bool p[Maxn];
int X[Maxn], Y[Maxn];
int _min ( int x, int y ){ return x < y ? x : y; }
int main (){
int i, j, k;
scanf ( "%d%d%d", &n, &m, &K );
l[0] = 0; u[0] = m+1;
for ( i = 1; i <= n; i ++ ){ scanf ( "%d%d", &X[i], &Y[i] ); l[i] = 0; u[i] = m+1; }
for ( i = 1; i <= K; i ++ ){
int x;
scanf ( "%d", &x );
p[x] = true;
scanf ( "%d%d", &l[x], &u[x] );
}
int st = 0;
for ( i = 0; i <= m; i ++ ) f[st][i] = 0;
int num = 0;
for ( i = 1; i <= n; i ++ ){
st = st^1;
for ( j = 0; j <= m; j ++ ) f[st][j] = inf;
bool bk = false;
for ( j = l[i-1]+1; j < u[i-1]; j ++ ){
if ( f[st^1][j] == inf ) continue;
bk = true;
f[st][_min(j+X[i],m)] = _min ( f[st][_min(j+X[i],m)], f[st^1][j]+1 );
}
for ( j = 0; j <= m; j ++ ){
if ( f[st][j] != inf ) f[st][_min(j+X[i],m)] = _min ( f[st][_min(j+X[i],m)], f[st][j]+1 );
}
for ( j = l[i-1]+1; j < u[i-1]; j ++ ){
if ( f[st^1][j] == inf ) continue;
if ( j-Y[i] > l[i] && j-Y[i] < u[i] ) f[st][j-Y[i]] = _min ( f[st][j-Y[i]], f[st^1][j] );
}
if ( bk == false ){ printf ( "0\n%d\n", num-1 ); return 0; }
if ( p[i] == true ) num ++;
}
int ans = inf;
for ( i = 1; i <= m; i ++ ) ans = _min ( ans, f[st][i] );
printf ( "1\n%d\n", ans );
return 0;
}
noip2013 货车运输
就是裸的最大瓶颈树..
通俗点说就是最大生成树再用st表维护一下路径最小值..
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int Maxn = 10010;
const int Maxm = 50010;
struct node {
int y, next, d;
}a[Maxn*2]; int first[Maxn], len;
void ins ( int x, int y, int d ){
len ++;
a[len].y = y; a[len].d = d;
a[len].next = first[x]; first[x] = len;
}
struct lnode {
int x, y, d;
}list[Maxm];
bool cmp ( lnode x, lnode y ){ return x.d > y.d; }
int n, m, q;
int fa[Maxn]; bool fw[Maxn];
int ff ( int x ){
if ( fa[x] == x ) return x;
return fa[x] = ff (fa[x]);
}
int minn[Maxn][15], fat[Maxn][15], dep[Maxn];
int _min ( int x, int y ){ return x < y ? x : y; }
void dfs ( int x, int f ){
fw[x] = true;
for ( int i = 1; i <= 14; i ++ ){
fat[x][i] = fat[fat[x][i-1]][i-1];
minn[x][i] = _min ( minn[x][i-1], minn[fat[x][i-1]][i-1] );
}
for ( int k = first[x]; k; k = a[k].next ){
int y = a[k].y;
if ( y == f ) continue;
fat[y][0] = x;
minn[y][0] = a[k].d;
dep[y] = dep[x]+1;
dfs ( y, x );
}
}
int lca ( int x, int y ){
int ret = 0x7fffffff;
if ( dep[x] < dep[y] ) swap ( x, y );
for ( int i = 14; i >= 0; i -- ){
if ( dep[fat[x][i]] >= dep[y] ){
ret = _min ( ret, minn[x][i] );
x = fat[x][i];
}
}
if ( x == y ) return ret;
for ( int i = 14; i >= 0; i -- ){
if ( fat[x][i] != fat[y][i] ){
ret = _min ( ret, minn[x][i] );
ret = _min ( ret, minn[y][i] );
x = fat[x][i]; y = fat[y][i];
}
}
return _min ( ret, _min ( minn[x][0], minn[y][0] ) );
}
int main (){
int i, j, k;
scanf ( "%d%d", &n, &m );
for ( i = 1; i <= m; i ++ ){
scanf ( "%d%d%d", &list[i].x, &list[i].y, &list[i].d );
}
sort ( list+1, list+m+1, cmp );
for ( i = 1; i <= n; i ++ ) fa[i] = i;
for ( i = 1; i <= m; i ++ ){
int fx = ff (list[i].x), fy = ff (list[i].y);
if ( fx != fy ){
fa[fy] = fx;
ins ( list[i].x, list[i].y, list[i].d );
ins ( list[i].y, list[i].x, list[i].d );
}
}
for ( i = 1; i <= n; i ++ ){
if ( fw[i] == false ){ dep[i] = 1; dfs ( i, 0 ); }
}
scanf ( "%d", &q );
for ( i = 1; i <= q; i ++ ){
int x, y;
scanf ( "%d%d", &x, &y );
int fx = ff (x), fy = ff (y);
if ( fx != fy ) printf ( "-1\n" );
else printf ( "%d\n", lca ( x, y ) );
}
return 0;
}
noip2015 跳石头
二分答案,然后找第一个距离超过这个答案的使用,扫一遍即可
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int Maxn = 50010;
int a[Maxn], n, m, L;
int main (){
int i, j, k;
scanf ( "%d%d%d", &L, &n, &m );
for ( i = 1; i <= n; i ++ ) scanf ( "%d", &a[i] );
a[++n] = L;
int l = 1, r = L, ret;
while ( l <= r ){
int mid = ( l + r ) >> 1;
int last = 0, sum = 0;
for ( i = 1; i <= n; i ++ ){
if ( a[i]-last >= mid ) last = a[i];
else sum ++;
}
if ( sum <= m ){ ret = mid; l = mid+1; }
else r = mid-1;
}
printf ( "%d\n", ret );
return 0;
}
noip2015 子串
哇我去年居然会做这道题 然而我现在好像不会了..
$f_{i,j,k}$表示现在在第$i$块,小串匹配到$j$,大串匹配到$k$的方案数
然后就可以这样搞:$$f_{i,j,k}=\sum\limits_{p=0}^{k-1}f_{i-1,j-1,p}+f_{i,j-1,k-1}\times (s2[j-1]==s1[k-1])$$
那么对于前面的只要维护一个前缀和就可以了..
再给一道题吧,也是前缀和相关:CodeForces 587B 记得当年就是从这题吸取了经验的(感谢胖涛..)
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int Maxn = 1010;
const int Maxm = 210;
const int Mod = 1000000007;
char a[Maxn], b[Maxm];
int n, m, K;
int f[2][Maxm][Maxn];
int main (){
int i, j, k;
scanf ( "%d%d%d", &n, &m, &K );
scanf ( "%s", a+1 );
scanf ( "%s", b+1 );
int st = 0;
for ( i = 0; i <= n; i ++ ) f[0][0][i] = 1;
for ( k = 1; k <= K; k ++ ){
st = st^1;
memset ( f[st], 0, sizeof (f[st]) );
for ( i = 1; i <= m; i ++ ){
for ( j = 1; j <= n; j ++ ){
if ( b[i] == a[j] ){
f[st][i][j] = ( f[st][i][j] + f[st^1][i-1][j-1] ) % Mod;
if ( b[i-1] == a[j-1] ) f[st][i][j] = ( f[st][i][j] + f[st][i-1][j-1] ) % Mod;
}
}
}
for ( i = 1; i <= m; i ++ ){
for ( j = 1; j <= n; j ++ ) f[st][i][j] = ( f[st][i][j] + f[st][i][j-1] ) % Mod;
}
}
printf ( "%d\n", f[st][m][n] );
return 0;
}
noip2015 运输计划
先预处理每条路径长度,找出最长的那条记为$ss$
二分答案,超过该答案的路径在树上差分标记一下,那么如果某个点子树所有标记和等于超过的路径数,证明该点与其父亲的边可能要割
然后在这些边中找到最大值,看$ss$减去这个值是否在二分的答案以内就行了
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int Maxn = 300010;
struct node {
int y, next, d;
}a[Maxn*2]; int first[Maxn], len;
void ins ( int x, int y, int d ){
len ++;
a[len].y = y; a[len].d = d;
a[len].next = first[x]; first[x] = len;
}
int n, m;
int fa[Maxn][23], sum[Maxn][23], dep[Maxn];
void dfs ( int x, int f ){
for ( int i = 1; i <= 22; i ++ ){
fa[x][i] = fa[fa[x][i-1]][i-1];
sum[x][i] = sum[x][i-1] + sum[fa[x][i-1]][i-1];
}
for ( int k = first[x]; k; k = a[k].next ){
int y = a[k].y;
if ( y == f ) continue;
fa[y][0] = x;
sum[y][0] = a[k].d;
dep[y] = dep[x]+1;
dfs ( y, x );
}
}
struct qnode {
int x, y, lca, dist;
}q[Maxn];
void get_lca ( qnode &p ){
int x = p.x, y = p.y;
p.dist = 0;
if ( dep[x] < dep[y] ) swap ( x, y );
for ( int i = 22; i >= 0; i -- ){
if ( dep[fa[x][i]] >= dep[y] ){
p.dist += sum[x][i];
x = fa[x][i];
}
}
if ( x == y ){ p.lca = x; return; }
for ( int i = 22; i >= 0; i -- ){
if ( fa[x][i] != fa[y][i] ){
p.dist += sum[x][i];
p.dist += sum[y][i];
x = fa[x][i];
y = fa[y][i];
}
}
p.dist += sum[x][0]+sum[y][0];
p.lca = fa[x][0];
}
int bj[Maxn];
int _max ( int x, int y ){ return x > y ? x : y; }
int DFS ( int x, int f, int mid ){
int ret = -1;
for ( int k = first[x]; k; k = a[k].next ){
int y = a[k].y;
if ( y == f ) continue;
ret = _max ( DFS ( y, x, mid ), ret );
if ( bj[y] == mid ) ret = _max ( ret, a[k].d );
bj[x] += bj[y];
}
return ret;
}
int main (){
int i, j, k;
scanf ( "%d%d", &n, &m );
for ( i = 1; i < n; i ++ ){
int x, y, d;
scanf ( "%d%d%d", &x, &y, &d );
ins ( x, y, d ); ins ( y, x, d );
}
dfs ( 1, 0 );
int ss = 0;
for ( i = 1; i <= m; i ++ ){
scanf ( "%d%d", &q[i].x, &q[i].y );
get_lca (q[i]);
ss = _max ( ss, q[i].dist );
}
int l = 1, r = ss, ret;
while ( l <= r ){
int mid = ( l + r ) >> 1;
int ssum = 0;
for ( i = 1; i <= n; i ++ ) bj[i] = 0;
for ( i = 1; i <= m; i ++ ){
if ( q[i].dist > mid ){
bj[q[i].x] ++; bj[q[i].y] ++;
bj[q[i].lca] -= 2;
ssum ++;
}
}
if ( ss-DFS ( 1, 0, ssum ) <= mid ){ ret = mid; r = mid-1; }
else l = mid+1;
}
printf ( "%d\n", ret );
return 0;
}
noip2013 华容道
记录f[i][j][k][l]为被标记的点在(i,j),空白点在k方向,使其移动到l方向的最小步数
这个东西是可以在$O(n^4)$预处理出来的
那么对于每一个询问,先算出空白点移动到标记点4个方向上的步数,然后就是最短路的事情了
推荐使用dijkstra算法,能够保证在$O(qn^2logn^2)$内算出..
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
using namespace std;
const int dx[4] = { 0, 1, -1, 0 };
const int dy[4] = { 1, 0, 0, -1 };
const int inf = 0x7fffffff;
int f[35][35][4][4], num[35][35][4], tot;
int dis[4010];
bool v[35][35];
int n, m, q;
int dist[35][35]; bool bo[35][35];
struct zb {
int x, y;
}e, s, t;
void bfs1 ( int xx, int yy, int k ){
queue <zb> q;
for ( int i = 0; i <= n+1; i ++ ) for ( int j = 0; j <= m+1; j ++ ) dist[i][j] = inf, bo[i][j] = false;
zb x;
x.x = xx+dx[k]; x.y = yy+dy[k];
q.push (x);
dist[x.x][x.y] = 0; bo[x.x][x.y] = true;
while ( !q.empty () ){
x = q.front (); q.pop ();
for ( int i = 0; i < 4; i ++ ){
zb y;
y.x = x.x+dx[i]; y.y = x.y+dy[i];
if ( v[y.x][y.y] == false || bo[y.x][y.y] == true ) continue;
dist[y.x][y.y] = dist[x.x][x.y]+1;
bo[y.x][y.y] = true;
q.push (y);
}
}
for ( int i = 0; i < 4; i ++ ){
f[xx][yy][k][i] = dist[xx+dx[i]][yy+dy[i]];
}
}
void bfs2 (){
queue <zb> q;
for ( int i = 0; i <= n+1; i ++ ) for ( int j = 0; j <= m+1; j ++ ) dist[i][j] = inf, bo[i][j] = false;
q.push (e);
dist[e.x][e.y] = 0; bo[e.x][e.y] = true;
while ( !q.empty () ){
zb x = q.front (); q.pop ();
for ( int i = 0; i < 4; i ++ ){
zb y;
y.x = x.x+dx[i]; y.y = x.y+dy[i];
if ( v[y.x][y.y] == false || bo[y.x][y.y] == true ) continue;
dist[y.x][y.y] = dist[x.x][x.y]+1;
bo[y.x][y.y] = true;
q.push (y);
}
}
}
struct node {
int y, next, d;
}a[160010]; int first[4010], len;
void ins ( int x, int y, int d ){
len ++;
a[len].y = y; a[len].d = d;
a[len].next = first[x]; first[x] = len;
}
struct knode {
int x, dis;
};
bool operator < ( knode x, knode y ){ return x.dis > y.dis; }
priority_queue <knode> Q;
void dij (){
while ( !Q.empty () ){
knode x = Q.top (); Q.pop ();
if ( dis[x.x] < x.dis ) continue;
for ( int k = first[x.x]; k; k = a[k].next ){
int y = a[k].y;
if ( dis[y] > dis[x.x]+a[k].d ){
dis[y] = dis[x.x]+a[k].d;
knode p;
p.x = y; p.dis = dis[y];
Q.push (p);
}
}
}
}
int _min ( int x, int y ){ return x < y ? x : y; }
int main (){
int i, j, k;
scanf ( "%d%d%d", &n, &m, &q );
for ( i = 1; i <= n; i ++ ){
for ( j = 1; j <= m; j ++ ){
int x;
scanf ( "%d", &x );
if ( x == 1 ) v[i][j] = true;
}
}
tot = 0;
for ( i = 1; i <= n; i ++ ){
for ( j = 1; j <= m; j ++ ){
if ( v[i][j] == false ) continue;
for ( k = 0; k < 4; k ++ ){
if ( v[i+dx[k]][j+dy[k]] == true ){
v[i][j] = false;
bfs1 ( i, j, k );
v[i][j] = true;
num[i][j][k] = ++tot;
}
else for ( int l = 0; l < 4; l ++ ) f[i][j][k][l] = inf;
}
}
}
while ( q -- ){
scanf ( "%d%d%d%d%d%d", &e.x, &e.y, &s.x, &s.y, &t.x, &t.y );
if ( s.x == t.x && s.y == t.y ){ printf ( "0\n" ); continue; }
v[s.x][s.y] = false;
bfs2 ();
v[s.x][s.y] = true;
for ( j = 1; j <= tot; j ++ ) first[j] = 0, dis[j] = inf;
len = 0;
for ( j = 0; j < 4; j ++ ){
if ( dist[s.x+dx[j]][s.y+dy[j]] != inf ){
knode p;
p.x = num[s.x][s.y][j]; p.dis = dist[s.x+dx[j]][s.y+dy[j]];
Q.push (p);
dis[num[s.x][s.y][j]] = dist[s.x+dx[j]][s.y+dy[j]];
}
}
for ( i = 1; i <= n; i ++ ){
for ( j = 1; j <= m; j ++ ){
for ( k = 0; k < 4; k ++ ){
if ( v[i+dx[k]][j+dy[k]] == true ){
for ( int l = 0; l < 4; l ++ ){
if ( f[i][j][k][l] != inf ) ins ( num[i][j][k], num[i][j][l], f[i][j][k][l] );
}
ins ( num[i][j][k], num[i+dx[k]][j+dy[k]][3-k], 1 );
}
}
}
}
dij ();
int ans = inf;
for ( i = 0; i < 4; i ++ ){
if ( num[t.x][t.y][i] != 0 ) ans = _min ( ans, dis[num[t.x][t.y][i]] );
}
if ( ans != inf ) printf ( "%d\n", ans );
else printf ( "-1\n" );
}
return 0;
}
noip2011 选择客栈
一开始看错题了.. 做法其实没啥区别就是强行解释一下
由于题目给出的$p$是不会变的,然后就可以瞎搞处理了,时间复杂度是$O(k*小于等于p的节点数)$
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int Maxc = 55;
int c[Maxc], ans[Maxc];
int n, K, p;
int main (){
int i, j, k;
scanf ( "%d%d%d", &n, &K, &p );
int anss = 0;
for ( i = 1; i <= n; i ++ ){
int fl, x;
scanf ( "%d%d", &fl, &x );
c[fl] ++;
if ( x <= p ){
for ( j = 0; j < K; j ++ ) ans[j] = c[j];
}
anss += ans[fl];
if ( x <= p ) anss --;
}
printf ( "%d\n", anss );
return 0;
}
noip2012 开车旅行
由于两人决策唯一性,那么可以预处理出从某点开始走$2^i$步到达的点..
讲道理的话其实也就只需要处理$f_{i,0}$和$f_{i,1}$,后面的都可以倍增求
第一问就枚举点倍增搞
其他问就直接跑..
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <set>
#define LL long long
using namespace std;
const int Maxn = 100010;
const int inf = 0x7fffffff;
struct no {
int x, h;
};
bool operator < ( no x, no y ){ return x.h < y.h; }
set <no> S;
set <no> :: iterator it;
int h[Maxn], n, m;
int fa[Maxn][22], fb[Maxn][22], f[Maxn][22];
no gob[Maxn];
void pre (){
int i, j, k;
no p;
p.x = n; p.h = h[n];
S.insert (p);
for ( i = n-1; i >= 1; i -- ){
p.x = i; p.h = h[i];
it = S.lower_bound (p);
no fir, sec;
fir.h = inf; sec.h = inf;
if ( it != S.end () ){
fir.x = (*it).x; fir.h = (*it).h-h[i];
it ++;
if ( it != S.end () ) sec.x = (*it).x, sec.h = (*it).h-h[i];
it --;
}
if ( it != S.begin () ){
it --;
if ( h[i]-(*it).h <= fir.h ){
sec = fir;
fir.x = (*it).x; fir.h = h[i]-(*it).h;
}
else if ( h[i]-(*it).h <= sec.h ){
sec.x = (*it).x; sec.h = h[i]-(*it).h;
}
if ( it != S.begin () ){
it --;
if ( h[i]-(*it).h <= sec.h ){
sec.x = (*it).x; sec.h = h[i]-(*it).h;
}
}
}
if ( sec.h != inf ){
f[i][0] = sec.x; f[i][1] = gob[sec.x].x;
fa[i][0] = fa[i][1] = sec.h;
fb[i][0] = 0; fb[i][1] = gob[sec.x].h;
for ( j = 2; j <= 21 && i+(1<<(j-1)) <= n; j ++ ){
f[i][j] = f[f[i][j-1]][j-1];
fa[i][j] = fa[i][j-1] + fa[f[i][j-1]][j-1];
fb[i][j] = fb[i][j-1] + fb[f[i][j-1]][j-1];
}
}
if ( fir.h != inf ) gob[i] = fir;
S.insert (p);
}
}
struct node {
int na, nb;
};
void gogogo ( int st, int xx, node &jl ){
jl.na = jl.nb = 0;
int i;
for ( i = 21; i >= 0; i -- ){
if ( fa[st][i]+fb[st][i] <= xx && f[st][i] != 0 ){
jl.na += fa[st][i];
jl.nb += fb[st][i];
xx -= fa[st][i]+fb[st][i];
st = f[st][i];
}
}
}
int main (){
int i, j, k;
scanf ( "%d", &n );
for ( i = 1; i <= n; i ++ ) scanf ( "%d", &h[i] );
pre ();
int x0, s0;
scanf ( "%d", &x0 );
s0 = n; node ans, po;
ans.na = ans.nb = 0;
for ( i = n-1; i >= 1; i -- ){
gogogo ( i, x0, po );
if ( (LL)po.na*ans.nb < (LL)ans.na*po.nb || ( (LL)po.na*ans.nb == (LL)ans.na*po.nb && h[i] > h[s0] ) || s0 == n || ( ans.nb == 0 && po.nb != 0 ) ){
s0 = i;
ans = po;
}
}
printf ( "%d\n", s0 );
scanf ( "%d", &m );
for ( i = 1; i <= m; i ++ ){
int ss, xx;
scanf ( "%d%d", &ss, &xx );
gogogo ( ss, xx, po );
printf ( "%d %d\n", po.na, po.nb );
}
return 0;
}
noip2011 观光公交
记录$time_i$是到达$i$点的最小时刻,$last_i$是最迟一个到达$i$点的人的时刻
那么$time_i=\max(time_{i-1},last_{i-1})+d_i$
可以处理出修改每一个$d_i$所影响的人数
每次贪心找一个最大的减去就好了
时间复杂度是$O(nk)$的..
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int Maxn = 100010;
int time[Maxn], d[Maxn], last[Maxn];
int n, m, K;
struct lnode {
int st, ed, t;
}list[Maxn];
int sum[Maxn], f[Maxn];
int _max ( int x, int y ){ return x > y ? x : y; }
int main (){
int i, j, k;
scanf ( "%d%d%d", &n, &m, &K );
for ( i = 2; i <= n; i ++ ) scanf ( "%d", &d[i] );
for ( i = 1; i <= m; i ++ ){
scanf ( "%d%d%d", &list[i].t, &list[i].st, &list[i].ed );
last[list[i].st] = _max ( last[list[i].st], list[i].t );
sum[list[i].ed] ++;
}
time[1] = 0;
for ( i = 2; i <= n; i ++ ){
time[i] = _max ( time[i-1], last[i-1] ) + d[i];
sum[i] += sum[i-1];
}
int ans = 0;
for ( i = 1; i <= m; i ++ ){
ans += time[list[i].ed] - list[i].t;
}
while ( K -- ){
f[n] = n;
for ( i = n-1; i >= 2; i -- ){
if ( time[i] > last[i] ) f[i] = f[i+1];
else f[i] = i;
}
int Max = 0, p;
for ( i = 2; i <= n; i ++ ){
if ( sum[f[i]]-sum[i-1] > Max && d[i] > 0 ){
Max = sum[f[i]]-sum[i-1];
p = i;
}
}
ans -= Max; d[p] --;
for ( i = 2; i <= n; i ++ ){
time[i] = _max ( time[i-1], last[i-1] ) + d[i];
}
}
printf ( "%d\n", ans );
return 0;
}