2024暑假集训测试25
前言
- 比赛链接。
一上来就感觉 T4 最可做,发现 T4 题面假了,去找学长改了,让后第一反应二分哈希,怎么调大样例都过不去,异常上火,狂调一个半小时才想起来丫的这玩意儿没有单调性,然后就崩了。
结果 T4 string 用死了挂成 \(0\) 分了还。
T2 是水板子但是不会那个板子,心态者了 T1 也没搞出来,T3 挂了 \(20\)。
T1 黎明与萤火
考虑叶子节点只有他爹能影响他,他爹要是删不掉就寄了,所以从深度大的往深度小的处理即可。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=2e5+10;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=true;
register char c=getchar();
for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x)
{if(x>9)wt(x/10);putchar((x%10)+'0');}
template<typename Tp> inline void write(Tp x)
{if(x<0)putchar('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar(' ');write(y...);}
int n,tot,top,fa[N],sta[N],deg[N],ans[N];
bool vis[N];
vector<int>e[N];
void dfs(int x,int father)
{
fa[x]=father,sta[++top]=x;
for(int y:e[x]) if(y!=father) dfs(y,x);
}
void dfs2(int x)
{
vis[x]=1,ans[++tot]=x;
for(int y:e[x])
{
deg[y]--;
if(y!=fa[x]&&!vis[y]&&!(deg[y]&1)) dfs2(y);
}
}
signed main()
{
read(n);
for(int i=1,x;i<=n;i++)
{
read(x);
if(x==0) continue;
e[x].push_back(i),e[i].push_back(x);
deg[x]++,deg[i]++;
}
dfs(1,0);
while(top)
{
int x=sta[top]; top--;
if(!(deg[x]&1)) dfs2(x);
}
puts(tot==n?"YES":"NO");
for(int i=1;i<=n&&tot==n;i++) write(ans[i]),puts("");
}
T2 Darling Dance
最短路树板子,对于 \(k\le n-1\) 时能贡献 \(k+1\) 个好点,即构成一棵树,所以最多只需要 \(n-1\) 条边,建出最短路树之后输出这些边即可。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=3e5+10,M=6e5+10;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=true;
register char c=getchar();
for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x)
{if(x>9)wt(x/10);putchar((x%10)+'0');}
template<typename Tp> inline void write(Tp x)
{if(x<0)putchar('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar(' ');write(y...);}
int n,m,k,cnt,pre[N];
ll dis[N];
bool vis[N];
int tot,head[N],to[M],nxt[M],w[M];
void add(int x,int y,int z)
{
nxt[++tot]=head[x];
to[tot]=y;
w[tot]=z;
head[x]=tot;
}
void dij()
{
memset(dis,0x3f,sizeof(dis));
priority_queue<pair<ll,int> >q;
dis[1]=0; q.push(make_pair(0,1));
while(!q.empty())
{
int x=q.top().second; q.pop();
if(vis[x]) continue;
vis[x]=1;
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];ll z=w[i];
if(dis[y]>dis[x]+z)
{
pre[y]=i;
dis[y]=dis[x]+z;
q.push(make_pair(-dis[y],y));
}
}
}
}
void dfs(int x)
{
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(i==pre[y])
{
if(++cnt>min(n-1,k)) exit(0);
write((i+1)>>1),putchar(' ');
dfs(y);
}
}
}
signed main()
{
read(n,m,k);
for(int i=1,x,y,z;i<=m;i++)
{
read(x,y,z);
add(x,y,z),add(y,x,z);
}
write(min(n-1,k)),puts("");
dij(),dfs(1);
}
T3 Non-breath oblige
-
部分分 \(10pts\):直接暴力线段树。
-
部分分 \(20pts\):对于没有操作 \(2\) 的直接全部输出 \(0\) 即可。
-
正解:
考虑加个时间戳,不难发现对于每个操作 \(3\) 只和他最近一个操作 \(2\) 有关,如果在没有找到最近的 \(2\) 之前存在操作 \(1\) 影响了他,直接将操作 \(3\) 的 \(x\) 替换成对应位置即可。
那么现在处理除了所有操作 \(3\) 的答案以及是哪一个位置对其产生影响的,定义其为 \(pos\),只有 \(pos\ge l\) 的他才会产生贡献,不难发现我们成功将其转化成了可差分信息,由此离线下来排序双指针加树状数组维护即可,每个询问就是 \(ask(r)-ask(l-1)\),不需要线段树、二维数点、扫描线等。
这个做法目前 OJ 和 luogu 上都是最优解,比第二的快 \(1s\) 多。
复杂度 \(O(q\log m)\)。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=1e6+10;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=true;
register char c=getchar();
for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
x=(z?x:~x+1);
}
template<typename T,typename ...Tp> inline void read(T &x,Tp &...y){read(x);read(y...);}
template<typename Tp> inline void wt(Tp x)
{if(x>9)wt(x/10);putchar((x%10)+'0');}
template<typename Tp> inline void write(Tp x)
{if(x<0)putchar('-'),x=~x+1;wt(x);}
template<typename T,typename ...Tp> inline void write(T x,Tp ...y){write(x);putchar(' ');write(y...);}
int n,m,k,tot,nxt[N];
ll ans[N];
struct aa {int op,l,r,x;}e[N];
struct bb {int id,pos,val;}c[N];
struct cc {int id,l,r;}q[N];
bool cmp1(bb a,bb b) {return a.pos>b.pos;}
bool cmp2(cc a,cc b) {return a.l>b.l;}
bb init(int id,int now,int pos)
{
for(int i=nxt[now],op,l,r,x;i;i=nxt[i])
{
op=e[i].op,l=e[i].l,r=e[i].r,x=e[i].x;
if(op==2&&l<=pos&&r>=pos) return {id,i,x};
if(op==1)
{
if(l==pos) return init(id,i,r);
if(r==pos) return init(id,i,l);
}
}
return {id,0,0};
}
struct BIT
{
ll c[N];
int lowbit(int x) {return x&-x;}
void add(int x,int d) {for(;x<=m;x+=lowbit(x)) c[x]+=d;}
ll ask(int x)
{
ll res=0;
for(;x;x-=lowbit(x)) res+=c[x];
return res;
}
}T;
signed main()
{
read(n,m,k); bool flag=0;
for(int i=1,op,l,r,x,now=0;i<=m;i++)
{
read(op);
l=r=x=0,flag|=(op==2),nxt[i]=now;
if(op==1||op==2) now=i;
if(op==1) read(l,r);
if(op==2) read(l,r,x);
if(op==3) read(x);
e[i]={op,l,r,x};
}
if(!flag)
{
for(int i=1;i<=k;i++) puts("0");
return 0;
}
for(int i=1;i<=m;i++) if(e[i].op==3)
c[++tot]=init(i,i,e[i].x);
for(int i=1,l,r;i<=k;i++) read(l,r),q[i]={i,l,r};
sort(c+1,c+1+tot,cmp1),sort(q+1,q+1+k,cmp2);
for(int i=1,j=1;i<=k;i++)
{
for(;j<=tot&&c[j].pos>=q[i].l;j++)
T.add(c[j].id,c[j].val);
ans[q[i].id]=T.ask(q[i].r)-T.ask(q[i].l-1);
}
for(int i=1;i<=k;i++) write(ans[i]),puts("");
}
T4 妄想感伤代偿联盟
暴力(闲话)部分
正解还没有打,是 AC 自动机,AC 自动机我学得不太好所以还没有打出来,先说部分分。
直接全暴力是扯淡的,发现把两个串拼起来后是个 \(border\),所以可以用 KMP 解决。
但是复杂度是假的,每次跑 \(\min(len_x,len_y)\),加个记忆化,最劣可以被卡到 \(n\sqrt n\),刚好过不去,能得 \(60pts\)。
改成哈希从大到小枚举,发现这玩意可能跑到一半就停了,但上面那玩意是跑满的,出题人良心一点答案不全是 \(0\) 就没事,但还是会被卡,因为出题人专门卡了哈希。
而且这个破数据还有 \(x,y>n\) 的,好多人哈希直接挂了(这个不怪哈希)。
选个舒服一点的模数,比如 \(1e9+3579\),不会被卡,能得 \(80pts\),当然需要记忆化,但是 unordered_map 常数太大了,改成值域分治,卡常卡的牛逼一点,有人直接暴力给过了。
代码就不放了,又不是正解。
-
upd:
暴力卡过了,甚至最优解。
怎么卡的呢,发现他只有前缀和后缀,所以建两个哈希就行了,不用每次 \(ask(l,r)\) 一遍,大大减小常数。
然后记忆化是过不去的,因为 unordered_map 常数巨大,发现 \(n\) 越小他长度越大啊,所以可以根号分治用数组存啊,其他过了的是这么干的,但是再考虑记忆化改成数组,然后判一下存不存的下就行了,跑得飞快。
当然是可以卡掉的,随便卡,不过数据虽然并不水但也挺水的。
总结
好像考的好不好全看 T1 能不能过。
T2 板子不会吃大亏了。
T4 不该死磕的,应该快点发现做法假了。
附录
果然每个人看到 T4 第一反应都是二分哈希:

这是官方题解。

浙公网安备 33010602011771号