CF1895E 题解
显然的,如果对方出了一张牌,那么自己一定要出在比这张牌防御值的大的牌中,防御值最大的牌。
如果不是,那么对手所可以接着出的牌就可能变多,那么这显然不优。
所以我们按照攻击值从小到大排序,然后预处理出后缀最大值和其出现位置,然后对于每张牌二分找到对手攻击力比这张牌防御力大的区间,然后找到防御力的最大值,将这两张牌连边。
这样就会构造出一个有向图,然后如果一个点的出度是
然后进行一次 dfs,因为一个点最多有一条出边,所以这个点的胜负状态就是他所连向的这个点的反面状态。
那么我们就能统计出必胜节点和必输节点,然后用总数减去必胜与必输的节点就是平局的节点数。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int t,n,m,d[600005],amax[600005],bmax[600005],posa[600005],posb[600005],ans[600005],vis[600005];
vector<int> ve[600005];
struct nd{
int x,y;
friend bool operator<(const nd&a,const nd&b){
return a.x<b.x;
}
}a[600005],b[600005];
void dfs(int u){
if(vis[u])return;
vis[u]=1;
for(int v:ve[u]){
dfs(v);
ans[u]=-ans[v];
}
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>t;
while(t--){
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i].x;
for(int i=1;i<=n;i++)cin>>a[i].y;
cin>>m;
for(int i=1;i<=m;i++)cin>>b[i].x;
for(int i=1;i<=m;i++)cin>>b[i].y;
for(int i=1;i<=n+m;i++)ve[i].clear(),d[i]=ans[i]=vis[i]=0;
sort(a+1,a+n+1);
sort(b+1,b+m+1);
amax[n+1]=bmax[m+1]=INT_MIN;
for(int i=n;i;i--){
if(amax[i+1]<a[i].y)amax[i]=a[i].y,posa[i]=i;
else amax[i]=amax[i+1],posa[i]=posa[i+1];
}
for(int i=m;i;i--){
if(bmax[i+1]<b[i].y)bmax[i]=b[i].y,posb[i]=i;
else bmax[i]=bmax[i+1],posb[i]=posb[i+1];
}
for(int i=1;i<=n;i++){
int aa=upper_bound(b+1,b+m+1,nd{a[i].y,0})-b;
if(aa!=m+1){
ve[i].emplace_back(posb[aa]+n);
d[i]++;
}
}
for(int i=1;i<=m;i++){
int bb=upper_bound(a+1,a+n+1,nd{b[i].y,0})-a;
if(bb!=n+1){
ve[i+n].emplace_back(posa[bb]);
d[i+n]++;
}
}
for(int i=1;i<=n+m;i++)if(!d[i])ans[i]=1;
for(int i=1;i<=n+m;i++)if(!vis[i])dfs(i);
int ans1=0,ans2=0;
for(int i=1;i<=n;i++)if(ans[i]==1)ans1++;else if(ans[i]==-1)ans2++;
cout<<ans1<<" "<<n-ans1-ans2<<" "<<ans2<<"\n";
}
return 0;
}