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;
}

下面是 O(n) 做法。

#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 提高组] 赛道修建

部分分:

  1. m=1 显然是直径。

  2. 链,我们将所有边按照顺序抓下来,二分答案+从上到下贪心即可。

  3. 对于菊花图,将所有边排序,1 边和 2m 边组合,2 边和 2m1 边组合,以此类推,这些边中的最小值即为答案。

    正确性:

    • 如果 n2m,说明可以全部组合,那么我们让最大的边和第 2m 大的边组合是一定优的。
    • 如果 n<2m,那么不能全部组合,我们需要留出一些最大的边,并用剩下的边两两配对。

这样是 55pts

100pts

我们用一个 multiset 来维护以 u 为根节点的子树中未匹配的链,设 fu 为子树内的最长未匹配的链

从小到大枚举每一条链,尝试在 multiset 中贪心二分查找匹配即可。

如果这条边匹配不上,那么 fumax 即可。

#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 提高组] 旅行

树是很好做的贪心,对于出边排序一下即可。

对于基环树,我们为每一个点的出边排序,并每次断掉一条边 O(n) check 即可。

这是 O(n2) 的。

#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 提高组] 保卫王国

44pts 非常好打,m 次没有上司的舞会即可。

#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;
}
posted @   Echo_Long  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示