题目链接:https://vjudge.net/problem/Gym-101142C
题意,给出n个人在两个网站上的排名,求一个人可能战胜多少个人。
x可能战胜y的条件,x至少有一个排名大于y。
可能战胜是可传递的,假如a可能战胜b,b可能战胜c,那么a也可能战胜c(即使c的两个排名都大于a)。
一开始对于可传递这一个条件没有读懂,以为需要直接可战胜才行。
于是先根据x,y从小到大排序,对于第i个人,把他可以战胜的人分为2类,第一类是x<=s[i].x的,第二类是x>s[i].x的。
对于第一类的话,肯定是i-1个(因为根据x,y排序了)。
对于第二类的话,倒序插入点建立权值树状数组,每次查询一下 [0 ,s[i].y)区间内有多少个元素就可以了。
然后wa3..........
之后重新读题很长一段时间,发现关系可传递。
那么对于第i个人来说,他能战胜的第一类人的数目没有发生变化,但是第二类人的数目可能增多。
例如
4
1 2
1 3
2 5
3 1
显然最后一个人可以战胜3个人,但是第一个人可以战胜最后一个人,所以第一个人也可以战胜三个人。
这里想的做法是,在找第二类人时不能用是s[i].y当做标准来找,应该用 max(s[0-i].y) 来找。
因为如果第i个人想战胜后面的人,一定是需要用y值来战胜他。而他可以借助他所战胜的第一类人里,最大的那个y值。
其实到这里已经很接近正确答案了,但是接下来的一步一直没有做出来。
我的猜想是,在【i+1,n】里,找一个尽可能靠后的小于max(s[1~i].y)的人,记录他的位置为k,那么i能战胜的人数就是k-1(减掉自己本身)。
但是这样依旧是错误的,例如:
3
1 5
2 11
3 4
3 10
对于第一个人,尽可能靠后的小于max(s[0-i].y)的人是3号,按照上面的做法那么一号能战胜的人数就是2。但是实际上一号可以通过战胜3号来战胜2号,再通过战胜2号来战胜4号,所以一号能战胜的人数为3。
比赛时想到这里已经不会做了,觉得或许可以通过建图来解决,建图的话一共1e5个点,但是没有细想如何连边,只是觉得有两个参数要处理,排序连完x后,y没反应过来该怎么弄.......
接下来是正确做法:
做法一:
首先排序,然后对于第i个人,预处理max(s[1~i].y)记录为s[i].my。接下来倒序遍历插入/查询树状数组,此时树状数组所维护的是区间最大值,查询y值在 1~s[i].my-1 之间的人能战胜的最多人数,用来和i-1进行比较取最大值。
练习赛时离正确算法只差这有点难逾越的一步,当时再往深里想思路就已经偏了。
这里稍微有一些dp思想
#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define pci pair<char,int>
#define mp make_pair
using namespace std;
const int mod = 1e9+7;
const int N = 1e6+1000;
struct node{
int x,y,my,id;
bool operator <(node b) const{
if(x!=b.x) return x<b.x;
return y<b.y;
}
}s[N];
int n,tree[N],ans[N];
int lb(int x) {
return x&-x;
}
void add(int x,int data) {
while(x<=N-500) {
tree[x] = max(tree[x],data);
x += lb(x);
}
}
int que(int x) {
int ans = 0;
while(x>0) {
ans = max(ans,tree[x]);
x -= lb(x);
}
return ans;
}
int main() {
freopen("codecoder.in","r",stdin);
freopen("codecoder.out","w",stdout);
ios::sync_with_stdio(0);
cin>>n;
rep(i, 1, n) {
cin>>s[i].x>>s[i].y;
s[i].id = i;
}
sort(s+1,s+n+1);
rep(i, 1, n)
s[i].my = max(s[i].y,s[i-1].my);
per(i, n, 1) {
ans[s[i].id] = max(i-1,que(s[i].my-1));
add(s[i].y,ans[s[i].id]);
}
rep(i, 1, n)
cout<<ans[i]<<endl;
return 0;
}
做法二:
建图缩点dfs
建图连边的方法:先以x为第一关键字排序依次连一遍,再以y为第一关键字排序连一遍。
比起做法一要好想一些。
#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define all(x) (x).begin(),(x).end()
using namespace std;
const int mod = 1e9+7;
const int N = 1e6+1000;
int n;
bool s_flag=0;
struct node{
int x,y,my,id;
bool operator <(node b) const{
if(s_flag) {
if(x!=b.x) return x<b.x;
return y<b.y;
}
else {
if(y!=b.y) return y<b.y;
return x<b.x;
}
}
}s[N];
struct Edge{
int v,nxt;
}edge[3*N];
int head[N],tot;
int low[N],dfn[N],Stack[N],belong[N]; //belong[x]表示x所属的强连通分量的编号
int idx,top;
int scc; //scc表示强连通分量的个数
bool instack[N];
int num[N]; //num[x]表示第x个强连通分量内所包含的点的个数
void addedge(int u,int v){
edge[tot].v = v;
edge[tot].nxt = head[u];
head[u] = tot++;
}
void tarjan(int u) {
int v;
low[u] = dfn[u] = ++idx;
Stack[top++] = u;
instack[u] = 1;
for(int i = head[u]; i != -1; i = edge[i].nxt) {
int v = edge[i].v;
if(!dfn[v]) {
tarjan(v);
if(low[u]>low[v]) low[u] = low[v];
}
else if(instack[v]&&low[u]>dfn[v]) low[u] = dfn[v];
}
if(low[u]==dfn[u]) {
scc++;
do {
v = Stack[--top];
instack[v] = 0;
belong[v] = scc;
num[scc]++;
}
while(v!=u);
}
}
void solve(int n) {
memset(dfn,0,sizeof(dfn));
memset(instack,0,sizeof(instack));
memset(num,0,sizeof(num));
idx = scc = top = 0;
rep(i, 1, n)
if(!dfn[i]) tarjan(i);
}
void init() {
tot = 0;
memset(head,-1,sizeof(head));
}
//以下是拓扑部分
vector<pii>road;
vector<int>nxt[N];
int ans[N];
bool vis[N];
int dfs(int u) {
if(ans[u]!=0) return ans[u];
ans[u] = num[u];
for(auto v:nxt[u])
ans[u] += dfs(v);
return ans[u];
}
int main() {
freopen("codecoder.in","r",stdin);
freopen("codecoder.out","w",stdout);
ios::sync_with_stdio(0);
cin>>n;
rep(i, 1, n) {
cin>>s[i].x>>s[i].y;
s[i].id = i;
}
init();
sort(s+1,s+n+1);
rep(i, 1, n-1) {
addedge(s[i+1].id,s[i].id);
road.pb(mp(s[i+1].id,s[i].id));
}
s_flag = 1;
sort(s+1,s+n+1);
rep(i, 1, n-1) {
addedge(s[i+1].id,s[i].id);
road.pb(mp(s[i+1].id,s[i].id));
}
solve(n);
memset(vis,0,sizeof(vis)); //初始化
rep(i, 1, scc) nxt[i].clear();
for(auto x:road) { //缩点重构图
int u = x.first;
int v = x.second;
int uu = belong[u];
int vv = belong[v];
if(uu!=vv)
nxt[uu].pb(vv);
}
rep(i, 1, scc) { //去重边
sort(all(nxt[i]));
nxt[i].erase(unique(all(nxt[i])),nxt[i].end());
}
rep(i, 1, n)
cout<<dfs(belong[i])-1<<endl;
return 0;
}