Tdog 2026 NOIP 模拟赛 Day5 部分题目题解

T1T2T3T4 都没过,遗憾离场 /ll。

T1 题解

有一个点数为 \(n\) 的图,\((i,j)\in E\) 当且仅当 \(p_i<p_j,v_i<v_j\),求 \(n\) 减去最小链剖分数。

\(1\le n \le 10^5,1\le p_i,v_i\le 10^9\)

首先按照 \(p\) 从小到大排序若相等按照 \(v\) 从大到小排序,那么容易发现每一条链都是一个上升子序列,那么答案即为 \(n\) 减去最长不上升子序列的长度,使用离散化 + 树状数组即可。

#include<bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
inline int read(){
	char c=getchar();
	int f=1,ans=0;
	while(c<48||c>57) f=(c==45?f=-1:1),c=getchar();
	while(c>=48&&c<=57) ans=(ans<<1)+(ans<<3)+(c^48),c=getchar();
	return ans*f;
}
const int N=1e5+10;
struct node{
	int p,v; 
	bool operator <(const node &x) const{if (p==x.p) return v>x.v;return p<x.p;}
}a[N];
int n,f[N],c[N];
vector<int>e;
inline int get(int x){return lower_bound(e.begin(),e.end(),x)-e.begin()+1;}
#define lowbit(x) x&-x
inline void add(int i,int x){for (;i<=n;i+=lowbit(i)) c[i]=max(c[i],x);}
inline int sum(int i){int ans=0;for (;i;i-=lowbit(i)) ans=max(ans,c[i]);return ans;} 
inline void solve(){
	n=read();e.clear(); 
	for (int i=1;i<=n;i++) a[i].p=read();for (int i=1;i<=n;i++) a[i].v=read(),e.push_back(a[i].v);
	sort(e.begin(),e.end()),e.erase(unique(e.begin(),e.end()),e.end()),sort(a+1,a+n+1); 
	for (int i=1;i<=n;i++) c[i]=f[i]=0;
	for (int i=n;i>0;i--) f[i]=sum(get(a[i].v))+1,add(get(a[i].v),f[i]);
	int ans=0;
	for (int i=1;i<=n;i++) ans=max(ans,f[i]);
	printf("%lld\n",n-ans);
}
main(){
	int T=read();
	while(T--) solve(); 
    return 0;
}

T2 题解

有一个无向图 \(G\),满足 \(V=\mathbb{N}\)\((i,j)\in E\) 当且仅当 \(i = jp,p\in \mathbb{P}\) 且边权为 \(p\),有 \(Q\) 次询问,每次给定 \(k\) 个点 \(x_1,x_2,\cdots,x_k\),求一个点 \(x\) 使得 \(x\)\(k\) 个点的距离的和最小,输出这个距离和以及有多少个点能取到这个距离和。

\(1\le Q \le 10^4,1\le \sum k \le 10^5,1 \le x_i \le 10^7\)

首先每一个质因子是独立的,可以分开单独计算,因此可以对每个数进行质因数分解。

对于每个质因数考虑,相当于这就是一个在数轴上找点使得到所有的点的距离最近,直接奇点偶段计算即可。

这里可以预处理所有 \(\le \sqrt {10^7}\) 的质数,这样基本上就不会被卡了。

#include<bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
inline int read(){
	char c=getchar();
	int f=1,ans=0;
	while(c<48||c>57) f=(c==45?f=-1:1),c=getchar();
	while(c>=48&&c<=57) ans=(ans<<1)+(ans<<3)+(c^48),c=getchar();
	return ans*f;
}
int p[3200],vis[3200],cnt; 
unordered_map<int,int>mp;
vector<int>a[3200];
inline void get(int n){
	for (int i=2;i<=n;i++){
		if (!vis[i]) p[++cnt]=i;
		for (int j=1;i*p[j]<=n;j++){
			vis[i*p[j]]=1;
			if (i%p[j]==0) break;
		}
	}
} 
inline void solve(){
	int ans1=0,ans2=1,n=read();
	mp.clear();
	for (int i=1;i<=cnt;i++) a[i].clear();
	for (int i=1;i<=n;i++){
		int x=read();
		for (int i=1;i<=cnt;i++){
			int cnt=0;
			while(x%p[i]==0) cnt++,x/=p[i];
			a[i].push_back(cnt);
		}
		if (x>1) mp[x]++;
	}
	for (int i=1;i<=cnt;i++){
		sort(a[i].begin(),a[i].end());
		int m=a[i].size();
		if (m&1){
			int x=a[i][m/2];
			for (auto j:a[i]) ans1+=abs(x-j)*p[i];
		}
		else{
			int x=a[i][m/2-1];
			for (auto j:a[i]) ans1+=abs(x-j)*p[i];
			ans2*=(a[i][m/2]-a[i][m/2-1]+1); 
		}
	}
	for (auto i:mp){
		int p=i.first,y=i.second;
		if (n&1){
			int x=n/2+1;
			if (n-y+1<=x) ans1+=p*(n-y);
			else ans1+=p*y;
		}
		else{
			int x=n/2;
			if (n-y+1<=x) ans1+=p*(n-y);
			else if (n-y+1>x+1) ans1+=p*y;
			else ans1+=p*(n-y),ans2*=2;  
		}
	}
	printf("%lld %lld\n",ans1,ans2);
}
main(){
	get(3200-1); 
	int T=read();
	while(T--) solve(); 
    return 0;
}

T3

有一个长度为 \(n\) 的数组 \(a\),有 \(m\) 次操作,每次给定 \(l,r,x\),使 \(a_i \leftarrow \max\{a_i,x\}\),求最后的数组 \(a\)

\(1\le n \le 10^5,1\le m \le 2\times 10^7\),有特殊的数据生成方式。

如果将修改改为询问最大值,那么直接 ST 表即可。

考虑将 ST 表的过程倒放,然后维护一下即可,\(O(n\log n +m )\)

#include<bits/stdc++.h>
#define int unsigned int
#define double long double
using namespace std;
const int N=1e5+10; 
inline int read(){
	char c=getchar();
	int f=1,ans=0;
	while(c<48||c>57) f=(c==45?f=-1:1),c=getchar();
	while(c>=48&&c<=57) ans=(ans<<1)+(ans<<3)+(c^48),c=getchar();
	return ans*f;
}
int f[N][21],lg[N];
int n,m,typ,x0,seed;
inline int get(){x0=(x0<<3)^x0,x0=((x0>>5)+seed)^x0;return x0;}
main(){
	n=read(),m=read(),typ=read(); 
	for (int i=1;i<=n;i++) lg[i]=log2(i),f[i][0]=read();
	x0=read(),seed=read();
	while(m--){
		int l=get()%n+1,r=get()%n+1,v=get();
		if (l>r) swap(l,r);
		if (typ==1) l=1;
		int k=lg[r-l+1];
		f[l][k]=max(f[l][k],v),f[r-(1<<k)+1][k]=max(f[r-(1<<k)+1][k],v);
	}
	for (int j=20;j>0;j--) for (int i=1;i+(1<<j)-1<=n;i++) f[i][j-1]=max(f[i][j-1],f[i][j]),f[i+(1<<j-1)][j-1]=max(f[i+(1<<j-1)][j-1],f[i][j]);
	for (int i=1;i<=n;i++) printf("%u ",f[i][0]);
    return 0;
}

T4 题解

有一个 \(n\) 个点 \(n-1\) 条边的树,每条边有一个字符作为边权,给定一个字符串 \(s\),有 \(m\) 个询问,询问分三种:

  1. 1 x\(x\) 的祖先中 \(x\) 到其的路径的字符组成的字符串是 \(s\) 的前缀的个数。

  2. 2 x\(x\) 的祖先中其到 \(x\) 的路径的字符组成的字符串是 \(s\) 的后缀的个数。

  3. 3 x p\(x\) 的后代中 \(x\) 到其的路径的字符组成的字符串是 \(p\) 的前缀个数。

多测。\(1\le T \le 5,1\le n,m,|s|,\sum |p|\le 10^5\)

首先这个 \(2\) 询问直接将 \(s\) 反转一下就和 \(1\) 同理了,不过多赘述。

考虑如何实现询问 \(1\),我们发现我们不能暴力爬祖先来判断,因此我们考虑对原树重剖然后跳重链判断即可,每个重链维护链底到链顶的路径的字符组成和链顶到其父亲的那个字符组陈的字符串,然后利用 hash + 二分判断即可。

考虑询问 \(3\),考虑离线将个 \(x\) 挂上所有的 \(p\),相当于就是要计算在 \(x\) 的子树内且 hash 值为 \(p\) 的前缀的 hash 个数,直接利用这个技巧即可。

#include<bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
inline int read(){
	char c=getchar();
	int f=1,ans=0;
	while(c<48||c>57) f=(c==45?f=-1:1),c=getchar();
	while(c>=48&&c<=57) ans=(ans<<1)+(ans<<3)+(c^48),c=getchar();
	return ans*f;
}
const int N=1e5+10,mod1=998244353,mod2=1e9+7,P=13331;
#define pic pair<int,char>
#define pii pair<int,int>
#define pis pair<int,string>
vector<pic>g[N];
int dfn[N],top[N],bot[N],sz[N],dep[N],id[N],idzl[N],viszl[N],son[N],idwz[N];
int cnt,idxzl,anss[N]; 
int n,m,r;
pic fa[N];
struct hash{
	string s;int n;
	vector<int>p1,p2,h1,h2;
	inline void init(){
		p1.clear(),p2.clear(),h1.clear(),h2.clear();
		n=s.size();s=" "+s;
		for (int i=1;i<=n+1;i++) p1.push_back(0),p2.push_back(0),h1.push_back(0),h2.push_back(0);
		p1[0]=p2[0]=1;
		for (int i=1;i<=n;i++) p1[i]=p1[i-1]*P%mod1,h1[i]=(h1[i-1]*P%mod1+s[i])%mod1,p2[i]=p2[i-1]*P%mod2,h2[i]=(h2[i-1]*P%mod2+s[i])%mod2;
	}
	inline pii gethash(int l,int r){return {(h1[r]-h1[l-1]*p1[r-l+1]%mod1+mod1)%mod1,(h2[r]-h2[l-1]*p2[r-l+1]%mod2+mod2)%mod2};}
}s[2],zl[N];
void dfs1(int u,int fa){
	sz[u]=1,dep[u]=dep[fa]+1;
	for (auto i:g[u]) if (i.first^fa){
		int v=i.first;char w=i.second;
		dfs1(v,u);
		sz[u]+=sz[v];
		if (sz[son[u]]<sz[v]) son[u]=v;
		::fa[v]={u,w};
	}
}
void dfs2(int u,int t){
	top[u]=t,dfn[u]=++cnt,bot[top[u]]=u;
	if (!son[u]) return ;dfs2(son[u],t);
	for (auto i:g[u]) if (i.first!=fa[u].first&&i.first!=son[u]) dfs2(i.first,i.first);
}
vector<pis>qry[N];
inline int ask(int x,int op){
	int pos=0,ans=0;
	while(x){
		int len=zl[idzl[top[x]]].n-idwz[x]+1;
		if (zl[idzl[top[x]]].gethash(idwz[x],zl[idzl[top[x]]].n)==s[op].gethash(pos+1,min(s[op].n,pos+len))) ans+=len,x=fa[top[x]].first,pos+=len;
		else{
			int l=idwz[x],r=zl[idzl[top[x]]].n,anss=l-1;
			while(l<=r){
				int mid=l+r>>1;
				if (zl[idzl[top[x]]].gethash(idwz[x],mid)==s[op].gethash(pos+1,pos+mid-idwz[x]+1)) anss=mid,l=mid+1;
				else r=mid-1;
			}
			return ans+anss-idwz[x]+1;
		} 
	}
	return ans;
}
int sum1[N],sum2[N];
map<pii,int>mp,mp1;
void dfs3(int u,int fa){
	for (auto i:qry[u]){
		int id=i.first;string w=i.second;
		int p1=1,p2=1,h1=0,h2=0;
		int cnt=0;
		for (auto i:w){
			p1=p1*P%mod1,p2=p2*P%mod2,h1=(h1*P%mod1+i)%mod1,h2=(h2*P%mod2+i)%mod2;
			mp1[{id,++cnt}]=mp[{(h1+sum1[u]*p1%mod1)%mod1,(h2+sum2[u]*p2%mod2)%mod2}];
		}
	}
	for (auto i:g[u]) if (i.first!=fa) sum1[i.first]=(sum1[u]*P%mod1+i.second)%mod1,sum2[i.first]=(sum2[u]*P%mod2+i.second)%mod2,dfs3(i.first,u); 
	for (auto i:qry[u]){
		int id=i.first;string w=i.second;
		int p1=1,p2=1,h1=0,h2=0;
		int cnt=0,ans=0;
		for (auto i:w){
			p1=p1*P%mod1,p2=p2*P%mod2,h1=(h1*P%mod1+i)%mod1,h2=(h2*P%mod2+i)%mod2;
			ans+=mp[{(h1+sum1[u]*p1%mod1)%mod1,(h2+sum2[u]*p2%mod2)%mod2}]-mp1[{id,++cnt}];
		}
		anss[id]=ans; 
	}
	mp[{sum1[u],sum2[u]}]++; 
}
inline void solve(){
	cnt=idxzl=0;mp.clear();
	n=read(),m=read(),r=read();string tmps;cin>>tmps;
	s[0].s=tmps,s[0].init(),reverse(tmps.begin(),tmps.end()),s[1].s=tmps,s[1].init();
	for (int i=1;i<=n;i++) g[i].clear(),viszl[i]=son[i]=idzl[i]=idwz[i]=sum1[i]=sum2[i]=0,qry[i].clear();
	for (int i=1;i<n;i++){
		int u=read(),v=read();char c;cin>>c;
		g[u].push_back({v,c}),g[v].push_back({u,c}); 
	}
	fa[r]={0,'#'},dfs1(r,0),dfs2(r,r); 
	for (int i=1;i<=n;i++) if (!viszl[top[i]]){
		idzl[top[i]]=++idxzl;
		string tmp="";
		int x=bot[top[i]],xx=0;
		while(x!=top[i]){if (fa[x].second!='#') idwz[x]=++xx,tmp+=fa[x].second,x=fa[x].first;}
		idwz[x]=++xx;if (fa[x].second!='#') tmp+=fa[x].second;
		zl[idxzl].s=tmp,zl[idxzl].init(); 
		viszl[top[i]]=1;
	}
	for (int i=1;i<=m;i++){
		int op=read(),x=read();
		if (op<=2) anss[i]=ask(x,op-1);
		else{string s;cin>>s;qry[x].push_back({i,s});}
	}
	dfs3(r,0);
	for (int i=1;i<=m;i++) printf("%lld\n",anss[i]);
}
main(){
	read(); 
	int T=read();
	while(T--) solve(); 
    return 0;
}
posted @ 2026-02-09 17:30  OTn53_qwq  阅读(19)  评论(0)    收藏  举报