B. Restore Atlantis
简单说下主要思路吧,具体实现还是要看代码。
把每个矩形看作时间轴上的事件,矩阵上每个位置被若干个矩阵覆盖,对每个位置求出时间轴上覆盖该位置的最小时间的矩阵及最大时间的矩阵。这个过程用扫描线的思想通过线段树和优先队列实现。
询问等价于这样一个问题,数轴上有若干个线段,询问包含的线段的个数。这个问题没想到在线做法,提出一个离线做法。大概就是先把所有右端点都打上标记,从左往右扫,逐渐把右端点的标记清除,同时进行区间查询。
这种题挺套路的,基本就是考虑时间轴,然后往线段树的每个节点塞一个数据结构。也做过几个类似的,不过依然不会这题,我真菜。
#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
这题比较神。
需要分析一波复杂度,对一棵大小是的完全二叉树,定义
是以
为根的子树的节点个数。那么
的大小是
。
因此对于每个节点,只通过子树内部的边跑一遍的总复杂度是
。
这道题就是先建立一棵完全二叉树,然后对于每个节点的子树跑一遍。这么做的正确性是最优的路径是起点到祖先到终点,一定会被包含到。
#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 ;
}
670

被折叠的 条评论
为什么被折叠?



