2024暑假集訓測試25

卡布叻_周深發表於2024-08-15

前言

  • 比賽連結

一上來就感覺 T4 最可做,發現 T4 題面假了,去找學長改了,讓後第一反應二分雜湊,怎麼調大樣例都過不去,異常上火,狂調一個半小時才想起來丫的這玩意兒沒有單調性,然後就崩了。

結果 T4 string 用死了掛成 \(0\) 分了還。

T2 是水板子但是不會那個板子,心態者了 T1 也沒搞出來,T3 掛了 \(20\)

T1 黎明與螢火

  • 原題:Destruction of a Tree

考慮葉子節點只有他爹能影響他,他爹要是刪不掉就寄了,所以從深度大的往深度小的處理即可。

點選檢視程式碼
#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\) 越小他長度越大啊,所以可以根號分治用陣列存啊,其他過了的是這麼幹的,但是再考慮記憶化改成陣列,然後判一下存不存的下就行了,跑得飛快。

    當然是可以卡掉的,隨便卡,不過資料雖然並不水但也挺水的。

總結

  • 詳見深痛教訓 2024.8.15

好像考的好不好全看 T1 能不能過。

T2 板子不會吃大虧了。

T4 不該死磕的,應該快點發現做法假了。

附錄

果然每個人看到 T4 第一反應都是二分雜湊:

image

這是官方題解。

相關文章