eJOI 2022 部分题解
eJOI 2022 部分题解
Adjacent Pairs
题意
求把原序列变成一个 形式() 的序列且过程中不出现两个相邻的数相等至少需要多少次单点赋值。
.
题解
简单的 做法:枚举奇数和偶数位分别是哪两个数,但是注意到如果过程中会出现 这种东西那还要额外增加 的操作。
怎么优化到 ? 对每个奇数位预处理出偶数位的答案和额外增加段的值。
怎么更快?额外增加段与奇数和偶数取什么没有关系,所以把那段预处理出来。那这样我们就只用关心奇数和偶数位取什么数,那贪心选需要改的最少(也即出现最多的)即可。
代码
查看代码
#include <bits/stdc++.h>
using namespace std;
template<typename T>void read(T &x)
{
T f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
x*=f;
}
map<int,int>M[200005];
int P[200005],arr[200005],C1[200005],C2[200005];
bool cmp(int a,int b){return C2[a]>C2[b];}
int main() {
#ifndef ONLINE_JUDGE
freopen("input","r",stdin);
freopen("output","w",stdout);
#endif
int T,n;read(T);
while(T--)
{
read(n);
for(int i=1;i<=n;i++) C1[i]=C2[i]=0;
for(int i=1,x;i<=n;i++)
{
M[i].clear();
read(arr[i]); P[i]=i;
if(i&1) C1[arr[i]]++;
else C2[arr[i]]++;
}
sort(P+1,P+1+n,cmp);
for(int i=1;i<=n;i++)
{
if(i&1) M[arr[i+1]][arr[i]]++;
else M[arr[i]][arr[i+1]]++;
if(arr[i]==arr[i+2]) i++;
}
int Ans=0;
for(int i=1;i<=n;i++)
{
for(auto j:M[i]) Ans=max(Ans,C1[i]+C2[j.first]-j.second);
for(int j=1;j<=n;j++)
{
if(!M[i].count(P[j])&&i!=P[j]) {Ans=max(Ans,C1[i]+C2[P[j]]);break;}
}
}
printf("%d\n",n-Ans);
}
return 0;
}
/*
清夜无尘。月色如银。酒斟时、须满十分。浮名浮利,虚苦劳神。叹隙中驹,石中火,梦中身。
虽抱文章,开口谁亲。且陶陶、乐尽天真。几时归去,作个闲人。对一张琴,一壶酒,一溪云。
*/
Where Is the Root?
题意
交互,给出大小为 的树,每次询问一个点集,返回其公共 是否存在于该点集中。要求 次询问找到根。、
保证存在一个点度数 。
.
题解
注意到如果选择了所有的叶子那其实我们可以断言:若返回YES则根必定在点集之中,否则必定不在点集之中。
显然 大概是 ,考虑二分。
每次二分的时候对一个 考虑,如果 没有包含所有叶子那就把 外所有叶子加入一起询问,每次递归仍然存在根的那边即可。
但是在只剩两个点并且还都是叶子的时候会出问题,所以我们假设若某个叶子是根,那把这个叶子放入不询问的集合,同时若当前不询问的集合旁边邻接的点度为 的点则把改点也加入不询问集合。之所以要不断更新是为了防止像下面这个数据一样:()
1 2
2 3
3 4
3 5
如果不询问集合只在 ,那询问 答案是 YES ,于是你就会以为另一个备选的叶子为根。
代码
查看代码
#include <bits/stdc++.h>
using namespace std;
template<typename T>void read(T &x)
{
T f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
x*=f;
}
int n,deg[505],P[505],Leaf;
bool cmp(int x,int y){return deg[x]<deg[y];}
vector <int> A;
void Ask()
{
printf("? %d",(int)A.size());
for(int i=0;i<(int)A.size();i++) printf(" %d",A[i]);
puts("");A.clear();
fflush(stdout);
}
int To[100005];
char op[10];
bool Ban[100005];
vector <int> G[100005];
void dfs(int u,int Fa)
{
// cerr<<x<<endl;
To[Fa]=u;
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i];
if(v==Fa) continue;
dfs(v,u);
}
}
void Solve(int l,int r)
{
// cerr<<l<<" "<<r<<endl;
if(l==r)
{
printf("! %d\n",P[l]);
fflush(stdout);
exit(0);
}
if(r-l+1<=2&&r<=Leaf)
{
int x=P[l];
// cerr<<x<<endl;
dfs(x,0);
while(deg[x]<=2)
{
// cerr<<x<<endl;
Ban[x]=true;
x=To[x];
}
Ban[x]=true;
for(int i=1;i<=n;i++) if(!Ban[i]) A.push_back(i);
Ask();scanf("%s",op+1);
if(op[1]=='Y') Solve(r,r);
else Solve(l,l);
}
int mid=(l+r)>>1;
for(int i=l;i<=mid;i++) A.push_back(P[i]);
for(int i=1;i<=Leaf&&i<l;i++) A.push_back(P[i]);
Ask(); scanf("%s",op+1);
if(op[1]=='Y') Solve(l,mid);
else Solve(mid+1,r);
}
int main() {
// #ifndef ONLINE_JUDGE
// freopen("input","r",stdin);
// freopen("output","w",stdout);
// #endif
read(n);
for(int i=1,u,v;i<n;i++)
{
read(u);read(v);
G[u].push_back(v);
G[v].push_back(u);
deg[u]++; deg[v]++;
P[i]=i;
To[u]=v; To[v]=u;
}
P[n]=n;
// for(int i=1;i<=n;i++) cerr<<To[i]<<" ";
sort(P+1,P+1+n,cmp);
// for(int i=1;i<=n;i++) cerr<<P[i]<<" ";
for(int i=1;i<=n;i++) if(deg[P[i]]==1&°[P[i+1]]>=2) {Leaf=i;break;}
// cerr<<Leaf<<endl;
Solve(1,n);
return 0;
}
/*
清夜无尘。月色如银。酒斟时、须满十分。浮名浮利,虚苦劳神。叹隙中驹,石中火,梦中身。
虽抱文章,开口谁亲。且陶陶、乐尽天真。几时归去,作个闲人。对一张琴,一壶酒,一溪云。
叶子是重要的
当一组询问包含了所有叶子并且为YES时那必然 救赎之道,就在其中!(大雾
询问一个区间:
纯非叶:YES不能说明根在其中,NO说明 必然不在其中
叶非叶混合:若叶子不完全同上,否则同下
纯叶:YES说明必然在其中,NO说明必然不在其中
前两个YES太假了怎么办:把所有叶子也拿来放进去
这样就是真正区分区间了
*/
Bounded Spanning Tree
题意
个点 条边的图,每条边有 之间的可选权值。要求给每条边赋权使得输入的前 条边可以成为该图的一个最小生成树,且所有边的权值是 的排列。求构造方案或说明无解。
.
题解
考虑克鲁斯卡尔的过程,那我们应该满足后面的边连接的两个端点在树上的路径权值最大值 该边权值。
所以我们考虑加强 的限制。具体来说先用倍增维护树上路径 最大值,然后对每条之后的边把它的权值下界赋为路径上的 最大值+1、对每条之后的边倍增把它的 拆分到树上路径边的最小值,然后具体到每条边上,这样树边的就是路径 最小值 。
现在每条边在 内任选都是合法的(前提是区间 是 内的且 ),所以我们只需要考虑能不能构造成排列。把每个离线到 上, 内部 按从小到大排序,并用 set
维护当前可用的权值。那么我们只需要 lower_bound
出最小的 的值即可。把这个值赋给这条边并从 set
里删除,这样贪心显然是正确的。
代码
查看代码
#include <bits/stdc++.h>
#define PII pair<int,int>
#define mp(a,b) make_pair(a,b)
using namespace std;
template<typename T>void read(T &x)
{
T f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
x*=f;
}
template<typename T>void print(T x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) print(x/10);
putchar(x%10+'0');
}
int T,n,m;
vector <PII> V[500005];
vector <PII> G[500005];
int l[500005],r[500005],lg[500005],dep[500005],Ans[500005];
int f[500005][21],Min[500005][21],Max[500005][21],FA[500005];
void dfs(int u,int fa)
{
f[u][0]=fa; dep[u]=dep[fa]+1;
for(int i=1;i<=lg[dep[u]];i++) f[u][i]=f[f[u][i-1]][i-1],Max[u][i]=max(Max[u][i-1],Max[f[u][i-1]][i-1]);
for(int i=0;i<(int)G[u].size();i++)
{
int v=G[u][i].first,id=G[u][i].second;
if(v==fa) continue;
Max[v][0]=l[id]+1; FA[v]=id;
dfs(v,u);
}
}
void Maintain(int x,int y,int id)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=lg[dep[x]];~i;i--)
{
if(dep[f[x][i]]>=dep[y])
{
l[id]=max(l[id],Max[x][i]);
Min[x][i]=min(Min[x][i],r[id]);
x=f[x][i];
}
}
if(x==y) return;
for(int i=lg[dep[x]];~i;i--)
{
if(f[x][i]!=f[y][i])
{
l[id]=max(l[id],max(Max[x][i],Max[y][i]));
Min[x][i]=min(Min[x][i],r[id]); Min[y][i]=min(Min[y][i],r[id]);
x=f[x][i]; y=f[y][i];
}
}
l[id]=max(l[id],max(Max[x][0],Max[y][0]));
Min[x][0]=min(Min[x][0],r[id]); Min[y][0]=min(Min[y][0],r[id]);
}
inline bool check(int L,int R){return L<=R&&L>=1&&R>=1&&L<=m&&R<=m;}
int main() {
#ifndef ONLINE_JUDGE
freopen("input","r",stdin);
freopen("output","w",stdout);
#endif
read(T);
for(int i=1;i<=500000;i++) lg[i]=lg[i>>1]+1;
while(T--)
{
read(n);read(m);
for(int i=1;i<=n;i++)
{
G[i].clear();
for(int j=0;j<=lg[n];j++) f[i][j]=Max[i][j]=0,Min[i][j]=1e9;
}
for(int i=1;i<=m;i++) V[i].clear();
for(int i=1,u,v;i<n;i++)
{
read(u);read(v);read(l[i]);read(r[i]);
G[u].push_back(mp(v,i)); G[v].push_back(mp(u,i));
}
dfs(1,0);//cerr<<"???"<<endl;
for(int i=n,u,v;i<=m;i++)
{
read(u);read(v);read(l[i]);read(r[i]);
Maintain(u,v,i);
}
for(int j=lg[n];j;j--)
{
for(int i=1;i<=n;i++)
{
Min[i][j-1]=min(Min[i][j-1],Min[i][j]);
Min[f[i][j-1]][j-1]=min(Min[f[i][j-1]][j-1],Min[i][j]);
}
}
for(int i=2;i<=n;i++) r[FA[i]]=min(r[FA[i]],Min[i][0]-1);
bool fail=false;bool Sure=true;
for(int i=1;i<=m;i++)
{
Sure&=(l[i]==r[i]);
if(!check(l[i],r[i])) {fail=true;break;}
V[r[i]].push_back(mp(l[i],i));
}
if(fail) {puts("NO");continue;}
set<int>Ava;
for(int i=1;i<=m;i++)
{
Ava.insert(i);
sort(V[i].begin(),V[i].end());
for(int j=0;j<(int)V[i].size();j++)
{
int L=V[i][j].first,id=V[i][j].second;
set<int>::iterator it;
if((it=Ava.lower_bound(L))==Ava.end()) {fail=true;break;}
Ans[id]=*it; Ava.erase(it);
}
if(fail) break;
}
if(fail) {puts("NO");continue;}
puts("YES");
for(int i=1;i<=m;i++) print(Ans[i]),putchar(' ');
puts("");
}
return 0;
}
/*
清夜无尘。月色如银。酒斟时、须满十分。浮名浮利,虚苦劳神。叹隙中驹,石中火,梦中身。
虽抱文章,开口谁亲。且陶陶、乐尽天真。几时归去,作个闲人。对一张琴,一壶酒,一溪云。
*/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
2020-11-02 Final Zadanie 题解