『模拟赛』暑假集训CSP提高模拟21
Rank
意外地还凑合,本来以为这场要 GG 了。
A. 黎明与萤火
原[CF963B] Destruction of a Tree
签,勉强签了。
开始脑子乱,胡乱打了一个含有 3 个 dfs 函数,每个函数里含两次遍历链式前向星,不负众望大样例 T 了。
后来也是摸着摸着就出正解了,先一遍 dfs 跑出基本的数据,然后再一遍 dfs 从叶子结点开始能删就删,删边操作单独进一个 dfs,最终判断能不能全删,然后输出就行了。
点击查看代码
已经优化很多了。。
#include<bits/stdc++.h>
#define fo(x,y,z) for(register int (x)=(y);(x)<=(z);(x)++)
#define fu(x,y,z) for(register int (x)=(y);(x)>=(z);(x)--)
using namespace std;
typedef long long ll;
#define lx ll
inline lx qr()
{
char ch=getchar();lx x=0,f=1;
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
return x*f;
}
#undef lx
#define qr qr()
const int Ratio=0;
const int N=2e5+5;
const int mod=998244353;
int n;
int fx[N],hh[N],to[N<<1],ne[N<<1],cnt;
int dfn[N],dff,pos[N],ans[N],siz[N],tot,rd[N];
bool yz[N];
namespace Wisadel
{
void Wadd(int u,int v)
{
to[++cnt]=v;
ne[cnt]=hh[u];
hh[u]=cnt;
}
void Wdfs(int u,int fa)
{
siz[u]=(fa!=0);
fx[u]=fa;
dfn[u]=++dff;
pos[dff]=u;
for(int i=hh[u];i!=-1;i=ne[i])
{
int v=to[i];
if(v==fa) continue;
Wdfs(v,u);
siz[u]+=1;
}
}
void Wsol(int u,int fa)
{
if(siz[u]&1) return;
if(yz[u]) return;
yz[u]=1,ans[++tot]=u;
for(int i=hh[u];i!=-1;i=ne[i])
{
int v=to[i];
if(yz[v]) continue;
if(v==fa){siz[v]--;continue;}
siz[v]--;
Wsol(v,u);
}
}
void Wdfss(int u,int fa)
{
for(int i=hh[u];i!=-1;i=ne[i])
{
int v=to[i];
if(v==fa) continue;
Wdfss(v,u);
}
Wsol(u,fa);
}
short main()
{
n=qr; memset(hh,-1,sizeof hh); bool task=1;
fo(i,1,n)
{
int x=qr; if(!x) continue;
Wadd(x,i),Wadd(i,x);
rd[i]++,rd[x]++;
if(rd[i]>2||rd[x]>2) task=0;
}
if(task)
{
if(n%2==0){printf("NO\n");return Ratio;}
printf("YES\n");
int st;
fo(i,1,n) if(rd[i]<2){st=i;break;}
Wdfs(st,0);
fo(i,1,n) if(i%2==0) printf("%d\n",pos[i]),yz[i]=1;
fo(i,1,n) if(!yz[i]) printf("%d\n",pos[i]);
return Ratio;
}
Wdfs(1,0);
Wdfss(1,0);
if(tot!=n) printf("NO\n");
else
{
printf("YES\n");
fo(i,1,tot) printf("%d\n",ans[i]);
}
return Ratio;
}
}
int main(){return Wisadel::main();}
B. Darling Dance
小水题,但保龄。
赛时想先进行一遍 Dijkstra,记录每个点的最短路由哪些边更新来,然后最后汇总,选更新次数多的。但一这个做法本身就有漏洞,二我没判 \(k\ge m\) 的情况,所以前面 WA 后面 RE。
正解是最短路径树,即用 \(n-1\) 条边使所有的点到根节点的距离尽可能小,恰好符合本题题意。由于最终结果是一棵树,所以除根节点外,每个结点恰好对应一条边,那么只要记一下每个点是由哪条边更新而来的,最后 dfs 输出即可。
点击查看代码
#include<bits/stdc++.h>
#define fo(x,y,z) for(register int (x)=(y);(x)<=(z);(x)++)
#define fu(x,y,z) for(register int (x)=(y);(x)>=(z);(x)--)
using namespace std;
typedef long long ll;
#define lx ll
inline lx qr()
{
char ch=getchar();lx x=0,f=1;
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
return x*f;
}
#undef lx
#define qr qr()
const int Ratio=0;
const int N=3e5+5;
const int mod=998244353;
int n,m,k,tot;
int hh[N],to[N<<1],ne[N<<1],id[N<<1],cnt;
ll w[N<<1],dis[N];
int ans[N];
bool yz[N];
struct rmm
{
int to;ll w;
bool operator<(const rmm & a) const
{
return w>a.w;
}
};
namespace Wisadel
{
void Wadd(int _,int __,int ___,int i)
{
to[++cnt]=__;
w[cnt]=___;
id[cnt]=i;
ne[cnt]=hh[_];
hh[_]=cnt;
}
void Wdij(int x)
{
priority_queue<rmm>q;
memset(dis,0x3f,sizeof dis);
dis[x]=0;q.push({x,0});
while(q.size())
{
int _=q.top().to;q.pop();
if(yz[_]) continue;
yz[_]=1;
for(int i=hh[_];i!=-1;i=ne[i])
{
int __=to[i];
if(dis[__]>=dis[_]+w[i])
{
dis[__]=dis[_]+w[i];
q.push({__,dis[__]});
ans[__]=id[i];
}
}
}
}
void Wdfs(int _)
{
for(int i=hh[_];i!=-1;i=ne[i])
{
int __=to[i];
if(id[i]==ans[__])
{
if(++tot>k||tot==n) exit(0);
printf("%d ",id[i]);
Wdfs(__);
}
}
}
short main()
{
n=qr,m=qr,k=qr;
memset(hh,-1,sizeof hh);
fo(i,1,m)
{
int _=qr,__=qr,___=qr;
Wadd(_,__,___,i),Wadd(__,_,___,i);
}
Wdij(1);
printf("%d\n",k>n-1?n-1:k);
Wdfs(1);
return Ratio;
}
}
int main(){return Wisadel::main();}
C. Non-breath oblige
还上 Ynoi 逝罢。
赛时只打出 \(\mathcal{O(nq)}\) 暴力和没有操作 2 那送的 20pts。
考虑由于出现操作 2 之前原序列不会有值,所以开线段树记录最后一次操作 2 的时间戳,每个操作就是简单的单点查询+修改,区间修改和单点查询。
将所有询问区间离线下来,对 r 做扫描线,遇到操作三就在 \(t_i\) 加上 \(v_{t_i}\) 并更新前缀,然后询问求后缀和即可,用树状数组维护,时间复杂度为 \(\mathcal{O(q\log n)}\)。
点击查看代码
#include<bits/stdc++.h>
#define fo(x,y,z) for(register int (x)=(y);(x)<=(z);(x)++)
#define fu(x,y,z) for(register int (x)=(y);(x)>=(z);(x)--)
using namespace std;
typedef long long ll;
#define lx ll
inline lx qr()
{
char ch=getchar();lx x=0,f=1;
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
return x*f;
}
#undef lx
#define qr qr()
#define fi first
#define se second
const int Ratio=0;
const int N=1e6+5;
const int mod=998244353;
int n,m,qq;
int op[N],x[N],y[N],va[N];
int t[N<<2];
ll ans[N],sum[N];
vector<pair<int,int> >q[N];
namespace Wisadel
{
#define ls (rt<<1)
#define rs (rt<<1|1)
#define mid ((l+r)>>1)
inline void Wpushdown(int rt)
{
t[ls]=t[rs]=t[rt];
t[rt]=0;
}
inline void Wupd(int rt,int l,int r,int x,int y,int k)
{
if(x<=l&&r<=y){t[rt]=k;return;}
if(t[rt]) Wpushdown(rt);
if(x<=mid) Wupd(ls,l,mid,x,y,k);
if(y>mid) Wupd(rs,mid+1,r,x,y,k);
}
inline int Wq(int rt,int l,int r,int x)
{
if(l==r||t[rt]) return t[rt];
if(x<=mid) return Wq(ls,l,mid,x);
else return Wq(rs,mid+1,r,x);
}
inline void Wadd(int x,int va)
{
if(!x) return;
while(x<=m) sum[x]+=va,x+=x&-x;
}
inline ll Wgsum(int x)
{
if(x<1) return 0;
ll res=0;
while(x) res+=sum[x],x^=x&-x;
return res;
}
short main()
{
// freopen(".in","r",stdin),freopen(".out","w",stdout);
n=qr,m=qr,qq=qr;
fo(i,1,m)
{
op[i]=qr;
if(op[i]==1)
{
x[i]=qr,y[i]=qr;
int aa=Wq(1,1,n,x[i]),bb=Wq(1,1,n,y[i]);
Wupd(1,1,n,x[i],x[i],bb),Wupd(1,1,n,y[i],y[i],aa);
}
else if(op[i]==2)
{
x[i]=qr,y[i]=qr,va[i]=qr;
Wupd(1,1,n,x[i],y[i],i);
}
else
{
x[i]=qr;
y[i]=Wq(1,1,n,x[i]);
}
}
fo(i,1,qq)
{
int l=qr,r=qr;
q[r].emplace_back(make_pair(l,i));
}
fo(i,1,m)
{
if(op[i]==3) Wadd(y[i],va[y[i]]);
for(auto j:q[i]) ans[j.se]=Wgsum(i)-Wgsum(j.fi-1);
}
fo(i,1,qq) printf("%lld\n",ans[i]);
return Ratio;
}
}
int main(){return Wisadel::main();}
D. 妄想感伤代偿联盟
做法好像还挺多,但是做到这时已经没啥心情打暴力了,然后直接糊了个超级 substr 上去,喜提保龄。
末
仍然很好题目背景,不过你都把歌曲名和歌词打出来了不放给我们听听真的合适吗qwq
赛时本来以为 T1 A不了 T2 能 A 的,结果刚好反了。因为打 T1 的时候脑子就很乱,胡乱交了个跑不过大样例的代码就去看后面的暴力了,后来强迫自己找性质结果莫名找出来了,但写得过于暴力一个代码 4 个 dfs 再加上 sb checker 乱报自测结果所以感觉拿不满,(事实上 CF 确实 WA 了一个小点。
T2 想到了只跑一遍 Dijkstra 的做法,爽飞了,大样例随便跑,sb checker 莫名报 UKE,但由于 T1 的前车之鉴我并不太信这个玩意,结果是经过赛后和 GGrun 的交谈发现是做法假了。
T3 昨天找题时看到过,但没多想(正常人都不会多想的吧,好在没挂分。
下午的锣鼓 Div1 继续挂挂挂,混个 rk73,有没有有经验的佬说一下等级分 1700 这个勾式排名会扣多少分。
完结撒花~