2025.8.16模拟赛
T1
判断是否存在一棵树,满足它有 \(a\) 个一度点和 \(b\) 个三度点,如果存在请给出一个节点数不超过 \(2000\) 的构造,否则输出 \(0\)。
\(0\le a,b\le 200\)。
- \(a=0,b=0\) 直接输出 \(1\)
- \(a=0,b\not = 0\) 显然不可能。
- \(a\not = 0,b=0\) 当 \(a=1\) 或 \(a=3\) 时无解,其余输出一个菊花即可。
剩余 \(a,b>0\)。
考虑将所有 \(b=3\) 组成一个二叉树,叶子结点用 \(a\) 填充。如果 \(a<b+2\) 无解,等于的情况根节点度数为 \(3\),构成一棵二叉树。可以发现 \(a=b+3\) 无解,\(a>b+3\) 直接二叉树,多余的 \(a\) 加到根节点作为菊花即可。
赛时代码
#include<bits/stdc++.h>
using namespace std;
const int N=1000;
int a,b;
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>a>>b;
if(!a&&!b)return cout<<1,0;
if(!a&&b)return cout<<0,0;
if(a&&!b){
if(a==1||a==3)cout<<0;
else{
cout<<a+1<<'\n';
for(int i=2;i<=a+1;++i)
cout<<1<<' '<<i<<'\n';
}return 0;
}if(a<b+2)return cout<<0,0;
if(a==b+2){
cout<<a+b<<'\n';
for(int i=1,s=1;i<=b;++i){
if(i==1)cout<<i<<' '<<++s<<'\n',
cout<<i<<' '<<++s<<'\n',
cout<<i<<' '<<++s<<'\n';
else cout<<i<<' '<<++s<<'\n',
cout<<i<<' '<<++s<<'\n';
}return 0;
}if(a==b+3)return cout<<0,0;
cout<<a+b+1<<'\n';
int s=1;
for(int i=1;i<=b+1;++i)
cout<<i<<' '<<++s<<'\n',
cout<<i<<' '<<++s<<'\n';
a-=b+2;
for(int i=1;i<=a;++i)
cout<<1<<' '<<++s<<'\n';
return 0;
}
T2
有一张 \(n\) 个点 \(m\) 条边的有向图,每条边上有一个正整数边权,要顺着图上的有向边从 \(1\) 号点走到 \(n\) 号点。假设你经过的边边权依次为 \(w_1 ,w_2\dots w_t\),则你的疲惫程度为 \(\max_{i=1}^t iw_i\)。求最小疲惫程度的路径长。
\(1\le n,m\le 3\times 10^5\)。
二分答案,然后记录一下每个点最早什么时候到,移动的时候判一下与 \(mid\) 大小关系即可。
赛时代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=3e5+5,F=0x3f3f3f3f;
int n,m,dis[N];
int head[N],tot;queue<int> q;
struct edge{int v,w,nxt;}e[N];
void add(int u,int v,int w){
e[++tot]=(edge){v,w,head[u]},head[u]=tot;
}bool check(ll x){
for(int i=2;i<=n;++i)
dis[i]=F;
q.push(1);
while(q.size()){
int u=q.front();q.pop();
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].v,w=e[i].w;
if(dis[v]==F&&1ll*w*(dis[u]+1)<=x)
dis[v]=dis[u]+1,q.push(v);
}
}return dis[n]!=F;
}signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1,u,v,w;i<=m;++i)
cin>>u>>v>>w,add(u,v,w);
ll l=0,r=3e14;
while(l<r){
ll mid=l+r>>1;
if(check(mid))r=mid;
else l=mid+1;
}cout<<r<<'\n';
return 0;
}
T3
一个 \(n\) 个点 \(m\) 条边的简单无向连通图,点从 \(0\sim n−1\) 编号,现在你需要删掉若干条边,最大化度数为奇数的点的个数。并给出构造,即输出一个长度为 m 的 01 串,1表示保留这个边,0表示删掉这个边,输出字典序最大的方案。
部分分:\(m=n-1\) 和 \(n\le 2000\)
100pts \(1\le n\le 600000,n-1\le m\le 900000\)。
赛时犯糖了。认为最大化奇点个数是用 dp 做,然后若强制一条边保留答案不变那么就可选,按顺序贪,每次做 dp。。。然后想到了对于图除最大生成树外都可选而树上还要做 dp。然后获得了 60pts。hehe
会发现对于树的情况,\(n\) 为偶数每个点由其连接父亲的那条边决定最终奇偶性,然后根节点必为奇点,方案是固定的;\(n\) 为奇数根节点必不为奇点,而对于不同的根共有 \(n\) 种结果,我们要最优化字典序。
对于有奇数个点的树,将其成为奇块,若有一条边的一端通向奇块,那么其等效于偶块。
维护奇偶性直接看最后一位的异或和。考虑分裂联通块从前到后考虑,如果不是生成树边直接选,并将两边块的块异或上 \(1\)。如果是树边。若两边都不是奇块,那么是真的无能为力。若至少一个奇块,那么直接选,剩下的留给后面的边考虑。再将块分出去。
赛后代码
#include<bits/stdc++.h>
#define eb emplace_back
using namespace std;
const int N=9e5+5;
int n,m,k,sum[N],deg[N];
int id[N],x[N],y[N];
vector<int> a[N],b[N];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;++i)
id[i]=i,a[i].eb(i);
for(int i=1;i<=m;++i)
cin>>x[i]>>y[i],++x[i],++y[i];
for(int i=m;i>=1;--i){
int u=id[x[i]],v=id[y[i]];
if(u!=v){
if(a[u].size()>a[v].size())swap(u,v);
b[i]=a[u];a[u].clear();
for(int x:b[i])a[v].eb(x),id[x]=v;
}
}sum[k=1]=n&1;
for(int i=1;i<=n;++i)
id[i]=1;
for(int i=1;i<=m;++i){
if(b[i].empty()){cout<<1;deg[x[i]]^=1,deg[y[i]]^=1;continue;}
int u=id[x[i]],tmp=0;
for(int x:b[i])tmp^=deg[x];
if(!(tmp&1)&&!(sum[u]&1))cout<<0;
else cout<<1,deg[x[i]]^=1,deg[y[i]]^=1,tmp^=1;
sum[u]^=tmp;sum[++k]=tmp;
for(int x:b[i])id[x]=k;
}
return 0;
}
T4
有一个 \(r\times c\) 的平面。有 \(n\) 个矩形在平面上和 \(q\) 个查询。每个查询给你一个矩形,询问这个矩形与给定的 \(n\) 个矩形相交的面积之并的面积。也可以认为是这 \(n\) 个矩形并的整个图形与给定矩形的交的面积。
\(1\le n,q\le 10^5\)。部分数据强制在线。
离线做这玩意是区间历史和,线段树维护一堆魔法懒标记。在线就是可持久化,十分的恐怖。没写。

浙公网安备 33010602011771号