풀이
[백준] 17131 - 여우가 정보섬에 올라온 이유 (c++)
땅왕
2025. 1. 28. 16:33
https://www.acmicpc.net/problem/17131
문제 요약
좌표 평면에 점 N개가 주어진다. 세점 s,t,u가 s.x<t.x<u.x이고 s.y>t.y<u.y를 만족하는 경우의 수를 구해라.
풀이 1 (NlogY)
- 세그먼트 트리
- 스위핑
처음에 떠오른 펜윅 두개를 관리하며 x축 스위핑을 하는 방법이다.
펜윅 하나는 y에 대해 y보다 큰 점의 개수를 관리하고 다른 하나는 t.y가 y보다 작은 s,t 쌍의 개수를 관리한다. 그럼 현재 점이 u일때 s,t,u 쌍의 개수를 구할 수 있다. x좌표가 겹치면 안되기 때문에 x좌표가 같은 점끼리 묶어서 처리해야 한다.
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N =4e5+5,mod=1e9+7;
ll up[N],down[N];
void uupdate(int x,int a) {
x+=2e5+1;
for(int i=x;i;i-=i&-i)
up[i]+=a;
}
ll usum(int x) {
x+=2e5+1;
ll ret=0;
for(int i=x;i<=N;i+=i&-i)
ret+=up[i];
return ret;
}
void dupdate(int x,int a) {
x+=2e5+1;
for(int i=x;i<=N;i+=i&-i)
down[i]+=a;
}
ll dsum(int x) {
x+=2e5+1;
ll ret=0;
for(int i=x;i;i-=i&-i)
ret+=down[i];
return ret;
}
int main() {
cin.tie(0)->sync_with_stdio(0);
int n;cin>>n;
vector<array<int,2>> v(n);
for(auto& [x,y]:v) cin>>x>>y;
sort(v.begin(),v.end());
ll ans=0;
for(int i=0;i<v.size();) {
//cout<<i<<'\n';
vector<array<int,2>> sx;
while(sx.empty()||(i<v.size()&&sx[0][0]==v[i][0]))
sx.push_back(v[i]),i++;
for(auto [x,y]:sx)
ans+=dsum(y),ans%=mod;
for(auto [x,y]:sx)
dupdate(y+1,usum(y));
for(auto [x,y]:sx)
uupdate(y-1,1);
}
cout<<ans;
}
풀이 2 (NlogX)
- 세그먼트 트리
- 스위핑
y축 스위핑을 하면 펜윅하나로도 풀 수 있다. 오름차순으로 스위핑하며 현재 점을 t로 두고 s의 개수*u의 개수를 구해주면 된다.
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N =4e5+5,mod=1e9+7;
ll bit[N];
void update(int x,int a) {
x+=2e5+1;
for(int i=x;i;i-=i&-i)
bit[i]+=a;
}
ll sum(int x) {
x+=2e5+1;
ll ret=0;
for(int i=x;i<=N;i+=i&-i)
ret+=bit[i];
return ret;
}
int main() {
cin.tie(0)->sync_with_stdio(0);
int n;cin>>n;
vector<array<int,2>> v(n);
for(auto& [y,x]:v) cin>>x>>y;
sort(v.rbegin(),v.rend());
ll ans=0;
for(int i=0;i<v.size();) {
vector<array<int,2>> sy;
while(sy.empty()||(i<v.size()&&sy[0][0]==v[i][0]))
sy.push_back(v[i]),i++;
for(auto [y,x]:sy)
ans+=sum(x+1)*(sum(-2e5)-sum(x)),ans%=mod;
for(auto [y,x]:sy)
update(x,1);
}
cout<<ans;
}