D
统计矩形
典,矩形只需要对角线两个点就能确定,枚举一组对角线,ds判断另一组对角线是否存在。这样一个矩形会被两组对角线分别枚举到,最后除二
void solve(){
int n;
cin>>n;
set<pii>s;
vi x(n+1),y(n+1);
rep(i,1,n){
cin>>x[i]>>y[i];
s.insert({x[i],y[i]});
}
int ans=0;
rep(i,1,n){
rep(j,i+1,n){
if(x[i]==x[j]||y[i]==y[j])continue;
if(s.count({x[j],y[i]})&&s.count({x[i],y[j]})){
ans++;
}
}
}
cout<<ans/2;
}
E
给一个图,可以删除一些边,但必须联通,删边有正收益或负收益,问最大收益?
如果只有正收益,总收益就是所有边的收益之和,减去最后留下来的边的收益,想让总收益尽可能大,就要让留下来的边的收益尽可能小,这其实就是最小生成树。
对于负收益,选了,总收益会变小。把他留在图里,连通性不会更差,所以都留在图里不删除。
void solve(){
int n,m;
cin>>n>>m;
vvi e;
int ans=0;
rep(i,1,m){
int u,v,w;
cin>>u>>v>>w;
e.push_back({w,u,v});
ans+=w;
}
sort(e.begin(),e.end());
vi f(n+1);
rep(i,1,n){
f[i]=i;
}
auto &&find=[&](auto &&find,int x)->int{
if(f[x]==x)return x;
return f[x]=find(find,f[x]);
};
for(auto &t:e){
int w=t[0],u=t[1],v=t[2];
int fx=find(find,u),fy=find(find,v);
if(w<0)ans-=w;
if(fx!=fy){
if(w>0)ans-=w;
f[fx]=fy;
}
}
cout<<ans;
}
F
一张图,对于删除每一条边的情况,求1到n的最短路
典,首先最短路有个结论,最短路上的边不会超过 n n n条,所以如果删除的不是最短路上的边,不会影响最短路,直接输出之前求出来的最短路。如果删除的是最短路上的边,重新跑一次最短路。
每次求最短路是 O ( n 2 ) O(n^2) O(n2),最多跑 O ( n ) O(n) O(n)次,总复杂度 O ( n 3 ) O(n^3) O(n3)
void solve(){
int n,m;
cin>>n>>m;
vi u(m+1),v(m+1);
vvi g(n+1);
rep(i,1,m){
cin>>u[i]>>v[i];
g[u[i]].push_back(v[i]);
}
vi pre(n+1);
auto cal=[&](int x,int y)->int{
vi d(n+1,1e9);
queue<int>q;
q.push(1);
d[1]=0;
while(q.size()){
int u=q.front();
q.pop();
for(int v:g[u]){
if(u==x&&v==y)continue;
if(d[v]>d[u]+1){
d[v]=d[u]+1;
pre[v]=u;
q.push(v);
}
}
}
return d[n];
};
int ans=cal(-1,-1);
int cur=n;
set<pii>s;
while(cur!=1&&pre[cur]){
s.insert({pre[cur],cur});
cur=pre[cur];
}
rep(i,1,m){
if(s.count({u[i],v[i]})){
int res=cal(u[i],v[i]);
if(res<1e9)cout<<res<<'\n';
else cout<<-1<<'\n';
}
else{
if(ans<1e9)cout<<ans<<'\n';
else cout<<-1<<'\n';
}
}
}