The 18th Zhejiang Provincial Collegiate Programming Contest B、D题解

B. Restore Atlantis

简单说下主要思路吧,具体实现还是要看代码。

把每个矩形看作时间轴上的事件,矩阵上每个位置被若干个矩阵覆盖,对每个位置求出时间轴上覆盖该位置的最小时间的矩阵及最大时间的矩阵。这个过程用扫描线的思想通过线段树和优先队列实现。

询问等价于这样一个问题,数轴上有若干个线段,询问[l,r]包含的线段的个数。这个问题没想到在线做法,提出一个离线做法。大概就是先把所有右端点都打上标记,从左往右扫,逐渐把右端点的标记清除,同时进行区间查询。

这种题挺套路的,基本就是考虑时间轴,然后往线段树的每个节点塞一个数据结构。也做过几个类似的,不过依然不会这题,我真菜。

#include<bits/stdc++.h>
using namespace std ;
using pq = priority_queue<int> ;
using pq2 = priority_queue<int , vector<int> , greater<int>> ;
const int maxn = 1e5 + 10 ;
vector<pair<int , int>> c[maxn] ;
int ans[maxn] ;
struct BIT
{
    int tree[maxn] ; 
    int n ;
    void init(int up)
    {
        n = up ;
    }
    int lowbit(int k)
    {
        return k & -k ;
    }
    void add(int x , int k)  // a[x] += k
    {
        while(x <= n)  //ά?????[1 , n] ????
        {
            tree[x] += k ;
            x += lowbit(x) ;
        }
    }
    int sum(int x)  // sum[l , r] = sum(r) - sum(l - 1)
    {
        int ans = 0 ;
        while(x != 0)
        {
            ans += tree[x] ;
            x -= lowbit(x) ;
        }
        return ans ;
    }
    int query(int l , int r)
    {
        if(l > r)  return 0 ;
        return sum(r) - sum(l - 1) ;
    }
} bit ;
struct Rec
{
    int xa , ya ;
    int xb , yb ;
    int id ;
} ;
struct Seg
{
    int cnt ;
    bool vis[maxn] ;
    pq q[maxn * 4] ;
    pq2 q2[maxn * 4] ;
    int lson(int id)
    {
        return id << 1 ;
    }
    int rson(int id)
    {
        return id << 1 | 1 ;
    }
    void add(int id , int l , int r , int x , int y , int k)
    {
        int mid = (l + r) / 2 ;
        if(r < x || y < l)  return ;
        if(x <= l && r <= y)
        {
            q[id].push(k) ;
            q2[id].push(k) ;
            return ;
        }
        add(lson(id) , l , mid , x , y , k) ;
        add(rson(id) , mid + 1 , r , x , y , k) ;
    }
    void dfs(int id , int l , int r , int mn , int mx)
    {
        while(!q[id].empty() && vis[q[id].top()])  q[id].pop() ;
        while(!q2[id].empty() && vis[q2[id].top()])  q2[id].pop() ;
        if(!q[id].empty())
        {
            mn = min(mn , q2[id].top()) ;
            mx = max(mx , q[id].top()) ;
        }
        if(l == r)
        {
            if(mn >= 1 && mn <= 1e6)
            {
                bit.add(mx , 1) ;
                c[mn].push_back({mx , 0}) ;
            }
            else  cnt ++ ;
            return ;
        }
        int mid = (l + r) / 2 ;
        dfs(lson(id) , l , mid , mn , mx) ;
        dfs(rson(id) , mid + 1 , r , mn , mx) ;
    }
    void cal(int n)
    {
        for(int i = 1 ; i <= n ; i ++)
        {
            for(auto u : c[i])
            {
                if(u.first < 0)  ans[u.second] = 2001 * 2001 - cnt - bit.query(i , -u.first) ;
                else  bit.add(u.first , -1) ;
            }
        }
    }
} seg ;
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    int n , q ;
    cin >> n >> q ;
    bit.init(n) ;
    vector<Rec> rec(n + 1) ;
    for(int i = 1 ; i <= n ; i ++)  
    {
        cin >> rec[i].xa >> rec[i].ya >> rec[i].xb >> rec[i].yb ;
        rec[i].id = i ;
    }
    vector<vector<int>> b(2000 + 10) ;
    vector<vector<int>> e(2000 + 10) ;
    for(int i = 1 ; i <= n ; i ++)  
    {
        b[rec[i].xa].push_back(i) ;
        e[rec[i].xb].push_back(i) ;
    }
    for(int i = 0 ; i <= 2000 ; i ++)
    {
        for(auto u : b[i])  seg.add(1 , 0 , 2000 , rec[u].ya , rec[u].yb - 1 , u) ;       
        for(auto u : e[i])  seg.vis[u] = true ;
        seg.dfs(1 , 0 , 2000 , 1e9 , -1) ;
    }
    for(int i = 1 ; i <= q ; i ++)
    {
        int s , t ;
        cin >> s >> t ;
        c[s].push_back({-t , i}) ;
    }
    for(int i = 1 ; i <= n ; i ++)  sort(c[i].begin() , c[i].end()) ;
    seg.cal(n) ;
    for(int i = 1 ; i <= q ; i ++)  cout << ans[i] << '\n' ;
    return 0 ;
}

 

D. Shortest Path Query

这题比较神。

需要分析一波复杂度,对一棵大小是n的完全二叉树,定义siz[i]是以i为根的子树的节点个数。那么\Sigma siz[i]的大小是O(n*logn)

因此对于每个节点,只通过子树内部的边跑一遍dij的总复杂度是O(n*logn*logn)

这道题就是先建立一棵完全二叉树,然后对于每个节点的子树跑一遍dij。这么做的正确性是最优的路径是起点到祖先到终点,一定会被包含到。

#include<bits/stdc++.h>
using namespace std ;
const int maxn = 1e5 + 10 ;
typedef pair<long long , int> pli ; //边权long long
int n , m ;
int dep[maxn] ;
long long dis[maxn][20] ;
vector<pair<int , int>> g[maxn] ;
struct Dij
{
    priority_queue<pli , vector<pli> , greater<pli> > q ;
    void dijkstra(int s)
    {
        dis[s][0] = 0 ;
        q.push(make_pair(0 , s)) ;
        while(!q.empty())
        {
            pli p = q.top() ;
            q.pop() ;
            int u = p.second ;
            if(p.first != dis[u][dep[u] - dep[s]])  continue ; //优化,不用旧值更新。
            for(auto x : g[u])
            {
                int v = x.first ;
                long long w = x.second ;
                if(dep[v] > dep[s] && dis[v][dep[v] - dep[s]] > dis[u][dep[u] - dep[s]] + w)
                {
                    dis[v][dep[v] - dep[s]] = dis[u][dep[u] - dep[s]] + w ;
                    q.push(make_pair(dis[v][dep[v] - dep[s]] , v)) ;
                }
            }
        }
    }
} dij ;
void add(int u , int v , int w)
{
    g[u].push_back({v , w}) ;
    g[v].push_back({u , w}) ;
}
void dfs(int u , int d)
{
    if(u > n)  return ;
    dep[u] = d ;
    dfs(u << 1 , d + 1) ;
    dfs(u << 1 | 1 , d + 1) ;
}
int main()
{
    std::ios::sync_with_stdio(false) , cin.tie(0) ;
    cin >> n >> m ;
    dfs(1 , 1) ;
    while(m --)
    {
        int u , v , w ;
        cin >> u >> v >> w ;
        add(u , v , w) ;
    }
    memset(dis , 0x3f , sizeof(dis)) ;
    for(int i = 1 ; i <= n ; i ++)  dij.dijkstra(i) ;
    int q ;
    cin >> q ;
    while(q --)
    {
        int s , t ;
        cin >> s >> t ;
        int ts = s , tt = t ;
        int ds = 0 , dt = 0 ;
        while(s != t)
        {
            if(dep[s] == dep[t])  s >>= 1 , ds ++ , t >>= 1 , dt ++ ;
            else
            {
                if(dep[s] < dep[t])
                {
                    t >>= 1 ;
                    dt ++ ;
                }
                else
                {
                    s >>= 1 ;
                    ds ++ ;
                }
            }
        }
        long long ans = 1e18 ;
        while(s >= 1)
        {
            if(dis[ts][ds] < 1e17 && dis[tt][dt] < 1e17)
                ans = min(ans , dis[ts][ds] + dis[tt][dt]) ;
            ds ++ ;
            dt ++ ;
            s >>= 1 ;
        }
        if(ans < 1e17)  cout << ans << '\n' ;
        else  cout << "-1\n" ;
    }
    return 0 ;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值