https://nanti.jisuanke.com/t/40259
题意:就是n个人比赛,每个人都有三种模式下的能力值,然后主持人主持比赛,可以任意选择两个人在同一模式比赛,能力值大的赢,输的人淘汰,直到最后只有一个人获胜。m次询问,问某个人最后是否可能赢。
题解:有顺序关系!!,如果看出强连通分量的话,难点就是建图了,先说怎么与强连通分量靠上(先拿暴力卡过去hh(其实也是同学教我的))。
为了让一个人最后赢,贪心的想每个模式排序后的最大能力值的人一定最后可能赢,所以下一个人想最后可能赢,他只要在其余两个模式下任意一个模式下赢了第一个赢的人,那他就会最后赢,后面的人依次这样操作。
又因为有三个模式所以顺序多了,每个人赢的顺序可以看成这样
(1,2,3表示人的编号,输指赢),每个人都可能是第一个赢的(最后赢),下一个人就会赢第一个人。所以建图就是每个模式排序,让输的指向赢的,三种形成一个大图,然后跑强连通分量
细节就是注意三个模式的最大能力者都是定位第一个赢的,那么强连通分量必须是有这三个人的强连通的分量
#include <cstdio>
#include <stack>
#include <cstring>
#include <iostream>
#include <vector>
#include<algorithm>
using namespace std;
const int maxn=2e5+10;
int n,m,idx=0,cnt=1,Bcnt=0;
int head[maxn*3];
int vis[maxn];
int dfn[maxn],low[maxn];
//int Belong[100];//belong表示每个点属于的缩完之后的哪一个点
stack <int> s;
vector<int>vec[maxn];
int vvv[maxn];
int na,nb,nc;
struct edge{
int v,next;
}e[maxn*3];
void adde(int u,int v){
e[cnt].next=head[u];
e[cnt].v=v;
head[u]=cnt++;
}
void tarjan(int u){
int v;
dfn[u]=low[u]=++idx;//每次dfs,u的次序号增加1
s.push(u);//将u入栈
vis[u]=1;//标记u在栈内
for(int i=head[u];i!=-1;i=e[i].next){//访问从u出发的边
v=e[i].v;
if(!dfn[v]){//如果v没被处理过
tarjan(v);//dfs(v)
low[u]=min(low[u],low[v]);//u点能到达的最小次序号是它自己能到达点的最小次序号和连接点v能到达点的最小次序号中较小的
}
else if(vis[v])
low[u]=min(low[u],dfn[v]);//如果v在栈内,u点能到达的最小次序号是它自己能到达点的最小次序号和v的次序号中较小的
}
if(dfn[u]==low[u]){//发现是整个强连通分量子树里的最小根。
Bcnt++;
do{
v=s.top();
if(v==na||v==nb||v==nc) vvv[Bcnt]=1;
s.pop();
vis[v]=0;//表示出栈
vec[Bcnt].push_back(v);
//Belong[v]=Bcnt;
}while(u != v);
}
}
struct node{
int v;
int id;
}a[maxn];
bool cmp(node a,node b){
return a.v>b.v;
}
int main(){
cin>>n>>m;
memset(head,-1,sizeof(head));
for(int i=1;i<=3;i++){
for(int j=1;j<=n;j++)
cin>>a[j].v,a[j].id=j;
sort(a+1,a+n+1,cmp);
vis[a[1].id]=1;
if(i==1) na=a[1].id;
if(i==2) nb=a[1].id;
if(i==3) nc=a[1].id;
for(int j=1;j<n;j++)
adde(a[j].id,a[j+1].id);//输家指赢家
}
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i);//保证每个点去找
for(int i=1;i<=Bcnt;i++){
if(vvv[i]){
int len=vec[i].size();
for(int j=0;j<len;j++){
vis[vec[i][j]]=1;
// cout<<i<<"---"<<vec[i][j]<<" ";
}
}
// cout<<endl<<endl;
}
while(m--){
int x;
cin>>x;
if(vis[x]) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
(直接就是现学的板子,套上就能用);