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 |
多练!!!!!

浙公网安备 33010602011771号