2025-11-07 NOIP 模拟赛3 赛后总结

赛时 Record

  • 8:11 读 T1。
  • 8:39 会了。发现自己糖糖了,T1 想了四十分钟。
  • 8:56 对拍了 \(60\) 组没有问题。开 T2。
  • 9:13 T2 好水啊。把速度压一下塞状态里建分层图就成 dij 板子了。
  • 9:41 过掉 T2 大洋里。发现自己连边连到层数上去了,/bangbangt。
  • 9:46 想到 T3 矩乘做法。把 \(a\times b\) 压进一行,然后处理出来转移矩阵,然后快速幂,然后就做完了。
  • 10:10 发现 \(op\)\(0,1,2\) 而不是 \(1,2,3\)
  • 10:46 卡了半个小时的常,极限数据还是只有 \(4.1\) 秒。结果发现 \(a\times b>25\) 的点不超过 \(3\) 个。
  • 11:03 疑似树上启发式合并。但我不会。
  • 11:06 哦我草 Nanatsukaze 新歌真空都市真好听。
  • 11:15 放弃正解,打了 70pts 暴力跑路了。

结果 T4 暴力没取模挂完了。

结果是 100+100+100+0。

T1 序列异或

题意

给定长为 \(n\) 的序列 \(a\),求满足 \(a_i\oplus a_j\oplus a_k\oplus a_l=0\) 的四元组 \((i,j,k,l)\) 的数量。

赛时

糖糖了,想了半天 \(O(n^2V)\) 解法。

题解

\(a_i\oplus a_j\oplus a_k\oplus a_l=0\) 显然等价于 \(a_i\oplus a_j = a_k\oplus a_l\)

所以对左边维护一个桶,记录所有二元组的异或的出现次数,每次对右边暴力扫一遍匹配即可。

时间复杂度 \(O(n^2)\)

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define infll 0x3f3f3f3f3f3f3f3f
using namespace std;

int n;
int a[5010];

long long bkt[1<<20];

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);

    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];

    long long ans=0;
    for(int i=2;i<=n-2;i++){
        for(int j=1;j<i;j++) bkt[a[i]^a[j]]++;
        for(int j=i+2;j<=n;j++) ans+=bkt[a[i+1]^a[j]];
    }
    cout<<ans<<"\n";

    # ifndef ONLINE_JUDGE
    cerr<<"\nUsed time: "<<clock()*1.0/CLOCKS_PER_SEC<<"s.\n";
    # endif
    return 0;
}

T2 自驾游

题意

\(n\) 个点,\(m\) 条单向边,有边权 \(d\)。初始速度为 \(v=1\),有 \(p\) 个特殊点,在特殊点 \(x_i\) 上可以花费 \(c_i\) 的时间,使速度翻倍,即 \(v\gets 2v\)

以速度 \(v\) 经过边权为 \(d\) 的边花费的时间为 \(\lceil \frac{d}{v}\rceil\)

每条边有特殊属性 \(w\)

  • \(w=0\):经过这条边后速度不会变化。
  • \(w=1\):经过这条边后速度变为 \(v=1\)

\(s\)\(t\) 的最短路。

赛时

一眼盯出把速度设进状态然后建分层图。

结果看了半天发现自己连边连错了。

题解

边权不超过 \(10^6\)。不难发现当速度足够大(\(2^{20}\))之后,经过一条边的时间是不会变的。

所以我们可以把当前点拆成 \(21\) 层(\(0\)\(20\)),第 \(k\) 层表示走下一条边的速度为 \(2^k\)

特殊属性为 \(1\) 的边显然都连向第 \(0\) 层的点,特殊点的速度翻倍自然就是向上一层连边。

所以建成分层图后跑一边最短路就行了。

时间复杂度 \(O(m\log n\log V)\)

注意空间开够。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define infll 0x3f3f3f3f3f3f3f3f
using namespace std;

// kn+b -> speed=v*2^k, id=b.

struct edge{int to,nxt;long long w;}e[2000010];int head[420010],cnt=1;
inline void addedge(int x,int y,long long w){e[cnt]={y,head[x],w},head[x]=cnt++;}

int n,m,S,T,adt;

struct node{
    int x;long long v;
    bool operator<(const node&_Q)const{return v>_Q.v;}
};priority_queue<node> q;
long long d[420010];
bool vis[420010];

void dij(){
    memset(d,0x3f,sizeof d);
    q.push({S,0});
    d[S]=0;
    while(!q.empty()){
        node fr=q.top();q.pop();
        int x=fr.x;
        if(vis[x]) continue;
        vis[x]=1;
        for(int i=head[x];i;i=e[i].nxt){
            int y=e[i].to;
            if(d[y]>d[x]+e[i].w){
                d[y]=d[x]+e[i].w;
                q.push({y,d[y]});
            }
        }
    }
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    
    cin>>n>>m>>S>>T;
    for(int i=1;i<=m;i++){
        int x,y,w;cin>>x>>y>>w;
        char c;cin>>c;
        if(c=='G') for(int j=0;j<=20;j++) addedge(j*n+x,j*n+y,(w+(1<<j)-1)/(1<<j));
        else for(int j=0;j<=20;j++) addedge(j*n+x,y,(w+(1<<j)-1)/(1<<j));
    }
    cin>>adt;
    for(int i=1;i<=adt;i++){
        int x;long long w;cin>>x>>w;
        for(int j=0;j<20;j++) addedge(j*n+x,(j+1)*n+x,w);
    }
    dij();

    long long ans=infll;
    for(int j=0;j<=20;j++) ans=min(ans,d[j*n+T]);
    if(ans==infll) cout<<"-1\n";
    else cout<<ans<<"\n";
    
    # ifndef ONLINE_JUDGE
    cerr<<"\nUsed time: "<<clock()*1.0/CLOCKS_PER_SEC<<"s.\n";
    # endif
    return 0;
}

T3 车马象

题意

在大小为 \(a\times b\) 的棋盘上,\((c,d)\) 有一个棋子,可能为车、马、象。

棋子可在棋盘中走 \(e\) 步,问总共有多少种可能的移动序列。

棋子走法和中国象棋中相同。

赛时

看到 \(a\times b\le 100\) 如此神奇的数据范围,第一反应不是矩乘就是状压。

然后想了想怎么转移。

然后发现 \(op\)\(0,1,2\) 而不是 \(1,2,3\)

卡了半个小时常才发现我的代码在正确的极限数据里只跑了 \(0.17\) 秒。

题解

注意到 \(e\le 10^9\),所以考虑矩乘。

\(a\times b\) 的棋盘重标号再拍扁到一维,然后考虑构造转移矩阵。

显然,原棋盘中两个互相可达的点可以互相转移。

所以就做完了。

时间复杂度 \(O(m(ab)^{3}\log e)\)

魔改了我自己的矩乘缺省源。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define infll 0x3f3f3f3f3f3f3f3f
using namespace std;

#ifdef ONLINE_JUDGE
#define getchar getchar_unlocked
#define putchar putchar_unlocked
#endif

template <typename Tp> static inline void read(Tp&x){
    int f=1;x=0;
    char c=getchar();
    while(c<'0'||c>'9') f=(c=='-'?-f:f),c=getchar();
    while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    x*=f;
}
template <typename Tp> static inline void print(Tp x){
    if(x<0) putchar('-'),x=-x;
    if(x<=9) putchar(x^48);
    else print(x/10),putchar((x%10)^48);
}

const long long mod=19260817;

struct matrix{
	int n,m;
	long long a[110][110];
	
	inline matrix(int _n=0,int _m=0){
        n=_n,m=_m;
        for(int i=1;i<=_n;i++) for(int j=1;j<=_m;j++) a[i][j]=0;
	}
	inline void reset(int _n=0,int _m=0){*this=matrix(_n,_m);}
	
	inline matrix operator*(const matrix&w)const{
		matrix ans(n,w.m);
		if(m!=w.n) return ans;
		for(int i=1;i<=n;i++) for(int j=1;j<=w.m;j++) for(int k=1;k<=m;k++) ans.a[i][j]=(ans.a[i][j]+a[i][k]*w.a[k][j]%mod)%mod;
		return ans;
	}
	inline void operator*=(const matrix&w){*this=*this*w;}
};

matrix mata,matb;

int main(){
    int Q;read(Q);
    while(Q--){
        int op,a,b,c,d,n;
        read(op),read(a),read(b),read(c),read(d),read(n);

        mata.reset(1,a*b);
        matb.reset(a*b,a*b);
        if(op==0){
            for(int x=1;x<=a*b;x++){
                int xrow=(x-1)/b+1;
                int xcol=(x%b==0?b:x%b);
                for(int y=x+1;y<=a*b;y++){
                    int yrow=(y-1)/b+1;
                    int ycol=(y%b==0?b:y%b);

                    if(xrow==yrow||xcol==ycol) matb.a[x][y]=matb.a[y][x]=1;
                }
            }
        }else if(op==1){
            for(int x=1;x<=a*b;x++){
                int xrow=(x-1)/b+1;
                int xcol=(x%b==0?b:x%b);
                for(int y=x+1;y<=a*b;y++){
                    int yrow=(y-1)/b+1;
                    int ycol=(y%b==0?b:y%b);

                    if((abs(xrow-yrow)==2&&abs(xcol-ycol)==1)||(abs(xrow-yrow)==1&&abs(xcol-ycol)==2)) matb.a[x][y]=matb.a[y][x]=1;
                }
            }
        }else{
            for(int x=1;x<=a*b;x++){
                int xrow=(x-1)/b+1;
                int xcol=(x%b==0?b:x%b);
                for(int y=x+1;y<=a*b;y++){
                    int yrow=(y-1)/b+1;
                    int ycol=(y%b==0?b:y%b);

                    if(abs(xrow-yrow)==2&&abs(xcol-ycol)==2) matb.a[x][y]=matb.a[y][x]=1;
                }
            }
        }
        mata.a[1][(c-1)*b+d]=1;
        while(n){
            if(n&1) mata*=matb;
            matb*=matb;
            n>>=1;
        }
        long long ans=0;
        for(int i=1;i<=a*b;i++) (ans+=mata.a[1][i])%=mod;
        print(ans);
        putchar('\n');
    }
    
    # ifndef ONLINE_JUDGE
    cerr<<"\nUsed time: "<<clock()*1.0/CLOCKS_PER_SEC<<"s.\n";
    # endif
    return 0;
}

总结

T4 场上想到了树上启发式合并。但很显然我不会。

然后去打了 \(O(n^2\log n)\) 的暴力,还忘了取模打挂了。

赛后问了 mhh 和 hxf 结果一个主席树一个线段树合并。但是很显然,我都不会写。

370 -> 300。

遗憾离场。

距离 AK 最近的一次。

posted @ 2025-11-07 15:19  AeeE5x  阅读(2)  评论(0)    收藏  举报