C. Flapping Takahashi
题意
初始高度为 H H H,可以上下移动,要求每秒高度变化不超过 1 1 1,且高度不能为负。给定 n n n 个限制,第 i i i 个限制为在第 t i t_i ti 秒,高度必须在 [ l i , u i ] [l_i,u_i] [li,ui] 之间。问是否存在一种可行方案。
思路
对于每个限制,若上次的高度区间已经固定,就可以确定这次能否到达。若上次的时间为 p r v prv prv,区间为 [ l , r ] [l,r] [l,r],则这次区间为 [ l − ( t i − p r v ) , r + ( t i − p r v ) ] [l-(t_i-prv),r+(t_i-prv)] [l−(ti−prv),r+(ti−prv)],判断该区间与 [ l i , u i ] [l_i,u_i] [li,ui] 是否有交集,并更新 [ l , r ] [l,r] [l,r],使 l ← max ( l , l i ) l\gets \max(l,l_i) l←max(l,li), r ← min ( r , u i ) r\gets \min(r,u_i) r←min(r,ui) 即可。
现在只要知道初始的高度区间即可,显然为 [ H , H ] [H,H] [H,H],即 l = r = H l=r=H l=r=H。
时间复杂度 O ( n ) \mathcal{O}(n) O(n)。
C++ 代码
cpp
#include<bits/stdc++.h>
#define int long long
using namespace std;
void solve(){
int n,h; cin>>n>>h;
int L=h,R=h,prv=0;
bool fl=1;
for(int i=1;i<=n;i++){
int t,l,r; cin>>t>>l>>r;
L-=(t-prv);
R+=(t-prv);
fl&=((R>=l&&r>=L));
L=max(L,l);
R=min(R,r);
prv=t;
}
if(fl) cout<<"Yes\n";
else cout<<"No\n";
}
main(){int T;cin>>T;while(T--)solve();}
D. Clouds
题意
有一个 2000 × 2000 2000\times 2000 2000×2000 的平面,有 n n n 个矩形,对于所有 1 ≤ i ≤ n 1\le i\le n 1≤i≤n,求:不被【除矩形 i i i 以外的任何矩形】覆盖的点有多少个。
思路
这题应该可以扫描线做,但是大可不必。
首先计算不去除任何矩形不覆盖的点的个数 a n s ans ans。然后考虑去除矩形 i i i 的贡献。我们发现,去除矩形 i i i 会增加不覆盖的点个数,当且仅当这个点仅被这一个矩形覆盖。所以我们可以对矩形覆盖位置做一个二维差分,记录那些位置只被覆盖一次,然后覆盖这个位置的矩形的贡献就加一。最后对于每个矩形的贡献 r e s i res_i resi,答案即为 a n s + r e s i ans+res_i ans+resi。
C++ 代码
cpp
#include<bits/stdc++.h>
using namespace std;
const int N=2005;
int a[N][N],cnt[N][N],cf[N][N],res[200005];
main(){
int n; cin>>n;
for(int i=1;i<=n;i++){
int u,d,l,r; cin>>u>>d>>l>>r;
cnt[u][l]++; cnt[u][r+1]--; cnt[d+1][r+1]++; cnt[d+1][l]--;
cf[u][l]+=i; cf[u][r+1]-=i; cf[d+1][r+1]+=i; cf[d+1][l]-=i; //差分记录覆盖它的id,若仅被覆盖一次,此值必为覆盖它的矩形编号
}
int ans=0;
for(int i=1;i<=2000;i++){
for(int j=1;j<=2000;j++){
cnt[i][j]+=cnt[i-1][j]+cnt[i][j-1]-cnt[i-1][j-1];
cf[i][j]+=cf[i-1][j]+cf[i][j-1]-cf[i-1][j-1];
ans+=(cnt[i][j]==0);
if(cnt[i][j]==1) res[cf[i][j]]++;
}
}
for(int i=1;i<=n;i++) cout<<ans+res[i]<<endl;
return 0;
}
E. Distribute Bunnies
题意
数轴上有 n n n 只兔子,第 i i i 只兔子要么站在 X i + R i X_i+R_i Xi+Ri,要么站在 X i − R i X_i-R_i Xi−Ri。问 n n n 只兔子位置的集合去重后的大小最大为多少?
思路
建图。把 X i − R i X_i-R_i Xi−Ri 和 X i + R i X_i+R_i Xi+Ri 连边(注意要把点离散化),问题转化为对于每条边,把其中一个端点涂黑,最多能涂黑多少个点。
考虑每个连通块,若大小为 s z sz sz。
- 若该连通块为一棵树,显然可以任意一点为根,每条边都涂黑深度较大的那个点,最终只剩下根节点,所以可以涂黑 s z − 1 sz-1 sz−1 个点。
- 若该连通块为一棵基环树,可以以环为根,每条边都涂黑深度较大的那个点,最终只剩下环上的点,显然环上的点可以都涂黑。所以可以涂黑 s z sz sz 个点。
- 若该连通块为一个无特殊性质的连通图,可以找到任意一棵生成树,再加一条边变为基环树,转化为上述情形,可以涂黑 s z sz sz 个点。
最终结论,不能涂黑的点数即为树的个数,答案即为总点数减去树的个数。
时间复杂度 O ( n log n ) \mathcal{O}(n\log n) O(nlogn),瓶颈在离散化。
C++ 代码
cpp
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n;
map<int,int> mp;
vector<int> g[N<<1];
bool used[N<<1];
int vcnt=0,edgecnt=0;
void dfs(int u){
used[u]=1;
vcnt++;
for(int v:g[u]){
edgecnt++;
if(!used[v]){
dfs(v);
}
}
}
int32_t main(){
int n; cin>>n;
int cnt=0;//总点数
for(int i=1;i<=n;i++){
int x,r; cin>>x>>r;
int a=x-r,b=x+r;
if(!mp[a]) mp[a]=++cnt;
if(!mp[b]) mp[b]=++cnt;
g[mp[a]].push_back(mp[b]);
g[mp[b]].push_back(mp[a]);
}
int ans=cnt;
for(int i=1;i<=cnt;i++){
if(!used[i]){
vcnt=edgecnt=0;
dfs(i);
ans-=(edgecnt/2==vcnt-1);//edgecnt/2 为实际边数,若边数==点数-1,该图为一棵树
}
}
cout<<ans<<endl;
return 0;
}