2018 NOIP 补题
P5019 [NOIP2018 提高组] 铺设道路
线段树应该是很一眼的做法。
考虑分治,对于每一个区间记录最小值的位置,直接计入答案并分治两边即可。
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define pb pop_back
#define mid (l+r>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define getchar() cin.get()
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define int long long
const int N = 1e5 + 5;
const int inf = 0x3f3f3f3f3f3f3f3f;
int read()
{
int f = 1 , x = 0;
char ch = getchar();
while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
int n , m , q , ans , a[N];
struct Tree
{
pii t[N<<2];
inl void up ( int p ) { t[p] = min ( t[ls] , t[rs] ); }
void build ( int p , int l , int r )
{
if ( l == r ) return t[p] = mkp ( a[l] , l ) , void();
build ( lson ) , build ( rson ) , up(p);
}
pii query ( int p , int l , int r , int x , int y )
{
if ( x <= l && r <= y ) return t[p];
pii res = mkp ( inf , 0 );
if ( x <= mid ) res = min ( res , query ( lson , x , y ) );
if ( mid + 1 <= y ) res = min ( res , query ( rson , x , y ) );
return res;
}
}T;
void solve ( int l , int r , int hei )
{
if ( l > r ) return;
if ( l == r ) return ans += a[l] - hei , void();
pii p = T.query ( 1 , 1 , n , l , r );
ans += p.fi - hei;
solve ( l , p.se - 1 , p.fi ) , solve ( p.se + 1 , r , p.fi );
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read();
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
T.build ( 1 , 1 , n );
solve ( 1 , n , 0 );
cout << ans << endl;
return 0;
}
下面是
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define pb pop_back
#define mid (l+r>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define getchar() cin.get()
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define int long long
const int N = 1e5 + 5;
const int inf = 0x3f3f3f3f3f3f3f3f;
int read()
{
int f = 1 , x = 0;
char ch = getchar();
while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
int n , m , q , ans , a[N];
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read();
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
for ( int i = 1 ; i <= n ; i ++ )
if ( a[i] > a[i-1] ) ans += a[i] - a[i-1];
cout << ans << endl;
return 0;
}
P5020 [NOIP2018 提高组] 货币系统
货币系统等价于让我们的所有能被前面的数表示出来的数都删掉,那么我们从小到大枚举数组,标记所有能筛掉的数即可。
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define pb pop_back
#define mid (l+r>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define getchar() cin.get()
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
const int N = 3e5 + 5;
int read()
{
int f = 1 , x = 0;
char ch = getchar();
while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
int n , a[N] , vis[N] , ans , maxx;
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
int T = read();
while ( T -- )
{
memset ( vis , 0 , sizeof vis );
ans = 0 , maxx = 0;
n = read();
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read() , maxx = max ( maxx , a[i] );
sort ( a + 1 , a + n + 1 );
for ( int i = 1 ; i <= n ; i ++ )
{
if ( vis[a[i]] ) continue;
vis[a[i]] = 1 , ++ ans;
for ( int j = 1 ; j <= maxx ; j ++ ) if ( vis[j] ) vis[j+a[i]] = 1;
}
cout << ans << endl;
}
return 0;
}
P5021 [NOIP2018 提高组] 赛道修建
部分分:
-
显然是直径。 -
链,我们将所有边按照顺序抓下来,二分答案+从上到下贪心即可。
-
对于菊花图,将所有边排序,
边和 边组合, 边和 边组合,以此类推,这些边中的最小值即为答案。正确性:
- 如果
,说明可以全部组合,那么我们让最大的边和第 大的边组合是一定优的。 - 如果
,那么不能全部组合,我们需要留出一些最大的边,并用剩下的边两两配对。
- 如果
这样是
我们用一个
从小到大枚举每一条链,尝试在
如果这条边匹配不上,那么
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define pb pop_back
#define mid (l+r>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define getchar() cin.get()
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
const int inf = 0x3f3f3f3f;
const int N = 3e5 + 5;
int read()
{
int f = 1 , x = 0;
char ch = getchar();
while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
int n , m , is1 = 1 , islian = 1 , summ;
vector<pii> e[N];
inl void add ( int u , int v , int w ) { e[u].eb(v,w); }
namespace sub1
{
int dis[N] , maxpos;
void dfs ( int u , int ff )
{
if ( dis[u] > dis[maxpos] ) maxpos = u;
for ( auto p : e[u] )
{
int v = p.fi , w = p.se;
if ( v == ff ) continue;
dis[v] = dis[u] + w;
dfs ( v , u );
}
}
void main()
{
dfs ( 1 , 0 );
int temp = maxpos; dis[maxpos] = 0;
dfs ( maxpos , 0 );
cout << dis[maxpos] << endl;
}
}
namespace sub2
{
vector<int> vec;
void dfs ( int u , int ff )
{
for ( auto p : e[u] )
{
int v = p.fi , w = p.se;
if ( v ^ ff ) vec.eb(w) , dfs ( v , u );
}
}
int check ( int x )
{
vec.clear();
dfs ( 1 , 0 );
int res = 0 , sum = 0;
for ( auto v : vec )
{
sum += v;
if ( sum >= x ) ++ res , sum = 0;
}
return res >= m;
}
void main ()
{
int l = 0 , r = summ;
while ( l <= r )
{
if ( check(mid) ) l = mid + 1;
else r = mid - 1;
}
cout << r << endl;
}
}
namespace sub3
{
int ans[N];
void main ()
{
int res = inf;
for ( int i = 1 ; i <= n ; i ++ ) for ( auto p : e[i] ) ans[p.fi-1] = p.se;
sort ( ans + 1 , ans + n , greater<int>() );
for ( int i = 1 ; i <= m ; i ++ )
res = min ( res , ans[2*m-i+1] + ans[i] );
cout << res << endl;
}
}
namespace sub4
{
int f[N];
int res = 0;
void dfs ( int u , int ff , int lim )
{
multiset<int> s;
for ( auto p : e[u] )
{
int v = p.fi , w = p.se;
if ( v ^ ff )
{
dfs ( v , u , lim );
if ( f[v] + w >= lim ) ++ res;
else s.insert ( f[v] + w );
}
}
while ( !s.empty() )
{
auto it = s.begin(); s.erase(it);
auto pos = s.lower_bound ( lim - *it );
if ( pos == s.end() ) f[u] = max ( f[u] , *it );
else ++ res , s.erase(pos);
}
}
int check ( int x )
{
memset ( f , 0 , sizeof f );
res = 0;
dfs ( 1 , 0 , x );
return res >= m;
}
void main ()
{
int l = 0 , r = summ;
while ( l <= r )
{
if ( check(mid) ) l = mid + 1;
else r = mid - 1;
}
cout << r << endl;
}
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , m = read();
for ( int i = 1 , u , v , w ; i < n ; i ++ )
{
u = read() , v = read() , w = read() , add ( u , v , w ) , add ( v , u , w );
if ( u + 1 != v ) islian = 0;
if ( u != 1 ) is1 = 0;
summ += w;
}
if ( m == 1 ) sub1::main();
else if ( islian ) sub2::main();
else if ( is1 ) sub3::main();
else sub4::main();
return 0;
}
P5022 [NOIP2018 提高组] 旅行
树是很好做的贪心,对于出边排序一下即可。
对于基环树,我们为每一个点的出边排序,并每次断掉一条边
这是
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define pb pop_back
#define mid (l+r>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define getchar() cin.get()
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
const int N = 1e5 + 5;
int read()
{
int f = 1 , x = 0;
char ch = getchar();
while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
int n , m , vis[N] , u[N] , v[N];
vector<int> e[N];
inl void add ( int u , int v ) { e[u].eb(v); }
namespace sub1
{
vector<int> ans;
void dfs ( int u , int ff )
{
ans.eb(u);
for ( auto v : e[u] )
if ( v ^ ff ) dfs ( v , u );
}
void main ()
{
for ( int i = 1 ; i <= m ; i ++ ) u[i] = read() , v[i] = read() , add ( u[i] , v[i] ) , add ( v[i] , u[i] );
for ( int i = 1 ; i <= n ; i ++ ) sort ( e[i].begin() , e[i].end() );
dfs ( 1 , 0 );
for ( auto v : ans ) cout << v << ' ';
}
}
namespace sub2
{
vector<int> cir , temp , ans;
int vis[N] , f[N] , now;
struct Dsu
{
int fa[N];
void init() { for ( int i = 1 ; i <= n ; i ++ ) fa[i] = i; }
int find ( int x ) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
void merge ( int u , int v ) { int fu = find(u) , fv = find(v); if ( fu != fv ) fa[fu] = fv; }
}D;
void dfs ( int u , int ff , int limu , int limv )
{
f[u] = ff;
for ( auto v : e[u] ) if ( v ^ ff && !( u == limu && v == limv ) && !( u == limv && v == limu ) ) dfs ( v , u , limu , limv );
}
void dfss ( int u , int ff , int limu , int limv )
{
temp.eb(u);
for ( auto v : e[u] ) if ( v ^ ff && !( u == limu && v == limv ) && !( u == limv && v == limu ) ) dfss ( v , u , limu , limv );
}
vector<int> min ( vector<int> a , vector<int> b )
{
for ( int i = 0 ; i < a.size() ; i ++ )
if ( a[i] < b[i] ) return a;
else if ( a[i] > b[i] ) return b;
return a;
}
void main()
{
for ( int i = 1 ; i <= m ; i ++ ) u[i] = read() , v[i] = read() , add ( u[i] , v[i] ) , add ( v[i] , u[i] );
for ( int i = 1 ; i <= n ; i ++ ) sort ( e[i].begin() , e[i].end() );
D.init();
for ( int i = 1 ; i <= m ; i ++ )
{
int fu = D.find(u[i]) , fv = D.find(v[i]);
if ( fu == fv ) dfs ( u[i] , 0 , u[i] , v[i] ) , now = v[i];
else D.merge ( u[i] , v[i] );
}
while ( now ) cir.eb(now) , now = f[now];
dfss ( 1 , 0 , cir.back() , *cir.begin() );
ans = temp;
for ( int i = 0 ; i < cir.size() - 1 ; i ++ )
{
temp.clear();
dfss ( 1 , 0 , cir[i] , cir[i+1] );
ans = min ( ans , temp );
}
for ( auto v : ans ) cout << v << ' ';
cout << endl;
}
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , m = read();
if ( m == n - 1 ) sub1::main();
else sub2::main();
return 0;
}
P5024 [NOIP2018 提高组] 保卫王国
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define inl inline
#define eb emplace_back
#define pb pop_back
#define mid (l+r>>1)
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define getchar() cin.get()
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define int long long
const int N = 5e5 + 5;
const int inf = 0x3f3f3f3f;
int read()
{
int f = 1 , x = 0;
char ch = getchar();
while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
int n , m , f[N][2] , a[N];
vector<int> e[N];
inl void add ( int u , int v ) { e[u].eb(v); }
void dfs ( int u , int ff )
{
for ( auto v : e[u] )
if ( v ^ ff )
{
dfs ( v , u );
f[u][1] += min ( f[v][0] , f[v][1] );
f[u][0] += f[v][1];
}
}
string str;
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , m = read();
cin >> str;
for ( int i = 1 ; i <= n ; i ++ ) a[i] = read();
for ( int i = 1 , u , v ; i < n ; i ++ ) u = read() , v = read() , add ( u , v ) , add ( v , u );
for ( int i = 1 ; i <= m ; i ++ )
{
int x = read() , opx = read() , y = read() , opy = read();
for ( int i = 1 ; i <= n ; i ++ )
{
if ( i == x ) f[i][opx] = opx ? a[i] : 0 , f[i][!opx] = inf;
else if ( i == y ) f[i][opy] = opy ? a[i] : 0 , f[i][!opy] = inf;
else f[i][0] = 0 , f[i][1] = a[i];
}
dfs ( 1 , 0 );
if ( min ( f[1][0] , f[1][1] ) >= inf ) cout << -1 << endl;
else cout << min ( f[1][0] , f[1][1] ) << endl;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!