E 单调栈 思维

移项得ai−i≥aj−j,bi−i≤bj−ja_i-i\geq a_j-j,b_i-i\leq b_j-jai−i≥aj−j,bi−i≤bj−j
设Ai=ai−i,Bi=bi−iA_i=a_i-i,B_i=b_i-iAi=ai−i,Bi=bi−i
AiA_iAi递增排列,BiB_iBi大的能和后面≤Bi\leq B_i≤Bi的组成连通块,不同连通块max(Bi)max(B_i)max(Bi)从前往后递增
cpp
void solve()
{
int n;cin>>n;
vector<pii>v(n+1);
forr(i,1,n){
cin>>v[i].aa>>v[i].bb;
v[i].aa-=i,v[i].bb-=i;
}
sort(v.begin()+1,v.end(),[&](pii x,pii y){//A_i递增排列
return (x.aa==y.aa?x.bb>y.bb:x.aa<y.aa);
});
vector<int>st;// 每个连通块的max(B_i) 单调递增栈
st.push_back(1);
forr(i,2,n){
if(st.size()){
if(v[st.back()].bb>=v[i].bb){//能跟栈顶(已有连通块中最大的B)相连
int tag=st.back(); // 选择 B_i 最大的作为一个联通集的标签 这样向后面连的线多
st.pop_back();
while(st.size()&&v[st.back()].bb>=v[i].bb)st.pop_back();//连了这个i点后 前面B>B_i的 也能靠i点和tag成为一个连通块
// st.push_back(i);
st.push_back(tag);// 如果放进i的话 直接是一个单减栈
}else{// v[st.back()].bb<v[i].bb 不能和已有的任何一个连通块相连
st.push_back(i);
}
}
}
cout<<st.size()<<endl;
}
F 贪心 堆优化

O(nm)O(nm)O(nm)做法,枚举最后获胜的人,然后枚举每场比赛,这个人比赛的最大值xi+zix_i+z_ixi+zi,其他人在其他比赛尽量小即zjz_jzj平均分配到xj、yjx_j、y_jxj、yj
可以看到其他人的得分重复计算了,可以用堆维护进行优化
cpp
const int N = 1e5+5,M=1e5;
const double PI=acos(-1);
const long long mod =998244353, inf = 1e18 ;
struct duel{
int x,y,z,id;
};
vector<duel>v[N];// 记录每个人参加的比赛
void solve(){
int n,m;cin>>n>>m;
forr(i,1,n)v[i].clear();
set<pii>p;// 用set作为堆 维护其他人的比赛中的得分 方便去掉所讨论的人参加的比赛
forr(i,1,m){
int a,b,x,y,z;
cin>>a>>b>>x>>y>>z;
v[a].push_back({x,y,z,i});
v[b].push_back({y,x,z,i});
// 贪心地想 一个人最大得分x+z 其他人尽量小 就是把其他人的比赛中的z平均分
int oth=max({(x+y+z+1)/2,x,y});
p.insert({oth,i});
}
vector<int>ans;
forr(i,1,n){// 讨论每个人参加的比赛
vector<pii>tmp;
int oth_mx=0,now_mx=0;
for(auto [x,y,z,id]:v[i]){
int oth=max({(x+y+z+1)/2,x,y});// 去掉参加的 保留没参加的
p.erase({oth,id});
tmp.push_back({oth,id});
oth_mx=max(oth_mx,y);
now_mx=max(now_mx,x+z);
}
if(p.size()){
oth_mx=max(oth_mx,(int)(prev(p.end())->first));
}
if(now_mx>oth_mx)ans.push_back(i);
for(auto x:tmp){
p.insert(x);
}
}
cout<<ans.size()<<endl;
for(auto x:ans)cout<<x<<' ';cout<<endl;
}