2025/5/5模拟赛总结

2025/5/5\(\mathbf{} \begin{Bmatrix} \frac{{\Large TEST} }{{\color{Yellow}\Large Record} }\mathbf{} {No.2} \end{Bmatrix}\times{}\) NeeDna

难度主观估计:${\color{Orange} t1} <{\color{Blue} t4} <{\color{Blue} t2} <{\color{Purple} t3} $

t1

开了51min,不是很好,一眼的可能性问题 + 二分变成判断型问题。结果用了贪心+堆+并查集写了好久:(

my code length: 1.2 KiB

template code length: 635 Bytes

t2

在30min时得到了正确的贪心,考虑剩下一部分的组合,2h未果,前面的判断也错了:(

典型错误:在 $ mod $ 之后继续比较大小!!

正确比较大数大小:

  • \(log\) :缺点,不能有加法。优点:好写(预处理 \(log\) )。

  • 哈希二分:二分比较哈希值是否相同,直到只剩下1位,然后直接比较数字大小。缺点:我写不来。

\(n\) 个数中分出 \([k,n]\) 个组的求法:插板法。

\(ans=\sum_{i=k-1}^{n-1}C_{n-1}^{i}\) 又因为 \(C_{m}^{n}=\frac{m!}{n!(m-n)!}\) 用逆元处理:\(a^{p-2}\equiv 1 \pmod{p}\)

神犇's code:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define yu (998244353)
#define N 2000100
#define ull unsigned long long
inline void add(ll &x,ll y){x+=y;if(x>=yu)x-=yu;return ;}
inline ll ksm(ll x,ll y=yu-2){
	ll an=1;for(;y;y>>=1){
		if(y&1)an=an*x%yu;
		x=x*x%yu;
	}return an;
}
ll n,k;
ll a[N];
ll jie[N],nijie[N];
inline ll c(ll x,ll y){
	return jie[x]*nijie[y]%yu*nijie[x-y]%yu;
}
struct node{
	ull bas;
	ull hs[N],pw[N];
	inline ull ask(ll x,ll y){
		return hs[y]-hs[x-1]*pw[y-x+1];
	}
	inline void init(){
		pw[0]=1;for(int i=1;i<=n;i++)pw[i]=pw[i-1]*bas;
		for(int i=1;i<=n;i++)hs[i]=hs[i-1]*bas+a[i];return ;
	}
}o1,o2;
int main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n>>k;string s;cin>>s;for(int i=0;i<n;i++)a[i+1]=s[i]-'0';ll ji=0;for(int i=1;i<=n;i++)ji+=a[i];
	if(n==k){
		ll an=0;for(int i=1;i<=n;i++)an+=a[i];
		cout<<an<<' '<<1<<'\n';
		return 0;
	}ll pos=1;while(pos<=n&&a[pos]==0)pos++;o1.bas=1e9+9;o2.bas=1e9+7;
	if(pos>=k){
		jie[0]=1;for(int i=1;i<=n;i++)jie[i]=jie[i-1]*i%yu;
		nijie[n]=ksm(jie[n]);for(int i=n-1;i>=0;i--)nijie[i]=nijie[i+1]*(i+1)%yu; 
		ll tt=0;for(int i=1;i<=n;i++)tt=(tt*2+a[i])%yu;	
		ll tem=pos-1,an=0;if(tem==n)tem--;
		for(int i=k-1;i<=tem;i++)add(an,c(tem,i));
		cout<<tt<<' '<<an<<'\n';
		return 0;
	}ll bg=n-k;pos=1;ll ans=1;o1.init();o2.init();
	for(int i=2;i<=n-bg;i++){
		ll l=1,r=bg,mid,an=0;
		while(l<=r){
			mid=(l+r)>>1;
			if(o1.ask(pos,pos+mid-1)==o1.ask(i,i+mid-1)&&o2.ask(pos,pos+mid-1)==o2.ask(i,i+mid-1))an=mid,l=mid+1;
			else r=mid-1;
		}if(an==bg)ans++;
		else{
			if(a[pos+an]<a[i+an])pos=i,ans=1;
		}
	}ll tt=0;for(int i=pos;i<=pos+bg;i++){
		tt=(tt*2+a[i])%yu;ji-=a[i];
	}add(tt,ji);
	cout<<tt<<' '<<ans<<'\n';
	return 0;
}

t3

目前没懂,但看到 \(n,m<1e3\) 可以做floyd。然后路径和 \(w_i<1e9\) 有关,所以用矩阵快速幂。

人类智慧:

  • \(n\) 小的时候,可以 \(O(n^2)\) 暴力解决或者Astar等。

  • \(w\) 大的时候,一条路径往返是很常见的,可使答案+2,可以在图上判断奇环来判断。

所以分类 + 数据水可以 \(AC\)

wisdom code:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e3+10;
vector<pair<int,int> >E[256];
int n,m,tot,head[N],vis[N],d[N],mxc;
struct node{
	int v,w,nxt;
}e[1000];
void add(int u,int v,int w){
	e[++tot]=node{v,w,head[u]},head[u]=tot;
}
void dfs(int u,int fa,int cost){
	if(vis[u]) return;
	if(cost>mxc+1000) return;
	d[u]=min(d[u],cost);
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].v,w=e[i].w;
		if(v==fa) continue;
		if(cost>=w)dfs(v,u,cost+1);
		else if(w>1500)dfs(v,u,w+1);
		else dfs(v,u,w+2);
	}
}
signed main(){
	memset(d,127,sizeof(d));
    cin>>n>>m;
    for(int i=1,u,v,d;i<=m;i++){
    	cin>>u>>v>>d;mxc=max(mxc,d);
		E[u].push_back(make_pair(v,d));
    	add(u,v,d);
	}
	if(mxc>80&&n>10){
		dfs(1,0,0);cout<<d[n];
	}
	else{
		queue<pair<int,int> >q;
		q.push(make_pair(1,0));
		while(!q.empty()){
			int u=q.front().first,dep=q.front().second;
			q.pop();
			for(pair<int,int> v:E[u]){
				if(dep<v.second)continue;
				if(v.first==n){
					cout<<dep+1;
					return 0;
				}
				q.push(make_pair(v.first,dep+1));
			}
		}
	}
	
	return 0;
} 

right code:

#include<bits/stdc++.h>
#define cs const
#define pb push_back
using namespace std;
cs int INF = 2e9;
cs int N = 155;
int n, m; int as = INF;
struct edge{ int u,v,w; } e[N];
struct mat{ 
	bitset<N> a[N];
	mat(){ for(int i=0; i<n; i++) a[i].reset(); }
	void I(){ for(int i=0; i<n; i++) a[i][i]=1; }
	mat operator * (cs mat &A){
		mat B; for(int i=0; i<n; i++)
		for(int j=0; j<n; j++) if(a[i][j]) B.a[i]|=A.a[j];
		return B;
	}
};

void work(cs mat &A, cs mat &E, int t){
	queue<int> q; static int d[N];
	memset(d,-1,sizeof(int)*(n+1));
	for(int i=0; i<n; i++) if(A.a[0][i]) q.push(i), d[i] = 0;
	while(!q.empty()){
		int x=q.front(); q.pop();
		for(int i=0; i<n; i++) if(d[i] == -1 && E.a[x][i]){
			d[i] = d[x] + 1; q.push(i);
		}
	} if(~d[n-1]) as = min(as, d[n-1] + t);
}
mat ksm(mat A, mat B, int b){ for(;b;b>>=1,B=B*B) if(b&1) A=A*B; return A; }

int main(){
	scanf("%d%d",&n,&m);
	mat A, E; A.a[0][0]=1;
	for(int i=1; i<=m; i++)
	scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w), --e[i].u, --e[i].v;
	
	sort(e+1, e+m+1, [](cs edge &a, cs edge &b){ return a.w < b.w; });
	for(int t=1,las=0,now; t<=m; t++){
		if(e[t].w>=as) break;
		now=e[t].w; if(now^las) A=ksm(A,E,now-las); 
		E.a[e[t].u][e[t].v]=1;
		work(A,E,now); las=now;
	}
	if(as==INF){ puts("Impossible"); return 0; } 
	cout<<as; return 0;
}

t4

题开晚了,每1h开1题。

正难则反维护连通性更加方便而且自然,还有断环为链。

用并查集时不用可撤销的而用栈存顺序,还可以做到 \(O(n)\) 撤回。

ac code:

#include<bits/stdc++.h>
using namespace std;const int N=1e3+2,fx[8][2]={{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};
int T,n,m,x,y,Q,f[2*N*N],g1[N*N],g2[N*N],tot,ans;bool k[N][2*N];char h;
inline int g(int i,int j){return (i-1)*2*m+j;}
inline int fnd(int x){return g1[++tot]=x,g2[tot]=f[x],(f[x]==x?x:f[x]=fnd(f[x]));}
inline void read(int &x){
	x=0,h=getchar();
	while(h<'0'||h>'9')h=getchar();
	while(h>='0'&&h<='9')x=(x<<1)+(x<<3)+(h^48),h=getchar();
}
inline void merge(int x,int y){x=fnd(x),y=fnd(y),g1[++tot]=x,g2[tot]=f[x],f[x]=y;}
inline void solve(int x,int y){
	int u,v;
	for(int i=0;i<8;i++){
		u=x+fx[i][0],v=y+fx[i][1];
		if(v>2*m)v=1;
		if(v<1)v=2*m;
		if(k[u][v])merge(g(x,y),g(u,v));
	}
}
int main(){
	read(T);
	while(T--){
		read(n),read(m),read(Q),ans=0,memset(k,0,sizeof(k));
		for(int i=1;i<=n;i++)for(int j=1;j<=2*m;j++)f[g(i,j)]=g(i,j);
		while(Q--){
			read(x),read(y),k[x][y]=k[x][y+m]=1,ans++,tot=0,solve(x,y),solve(x,y+m);
			if(fnd(g(x,y))==fnd(g(x,y+m))){
				for(int i=tot;i>=1;i--)f[g1[i]]=g2[i];
				ans--,k[x][y]=k[x][y+m]=0;
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}

Conclusion:

估分 实际分 实际做的分 实际会的分
270 190 120 250

多练!!!!!

posted @ 2025-06-28 14:22  NeeDna  阅读(17)  评论(0)    收藏  举报