2 条题解

  • 0
    @ 2026-9-24 23:52:14

    废话时间

    这题思维上可能算不上紫,但是考虑到 模板 exCRT 也是紫,所以评紫应该是没问题。

    正文

    首先让我们来观察一下本题,第 ii 轮报数的时候,不难发现当圈中还剩下 n−i+1n-i+1 个人,我们记 Ni=n−i+1N_i=n-i+1。如果第 ii 个人报了 ss,那么这个人报的数就一定是 s+t×Nis+t\times N_i,其中 tt 是整数。

    那对于第 ii 轮出局的人 tit_i,我们记这个人报了 stis_{t_i},那么必有 K≡sti(modNi)K\equiv s_{t_i} \pmod {N_i}。

    显然对于每一轮都能产生一个同余方程,那么我们就能获得一个方程个数为 nn 的同余方程组:

    $$\left\{ \begin{array}{l} K \equiv s_{t_1} \pmod{N_1} \\ K \equiv s_{t_2} \pmod{N_2} \\ \ \ \vdots \\ K \equiv s_{t_n} \pmod{N_n} \end{array} \right.$$

    而这个方程组显然可以用 exCRT 求解,方程组无解则是问题无解。

    接下来我们考虑如何求出 stis_{t_i}。其实这个就相当于当前圈起点到当前点的距离,而根据规则,当前圈的起点应该是上一圈淘汰者的后一个位置,也就是说,我们可以求出 ti−1t_{i-1} 到 tit_i 的距离。特别的,我们规定 t0=0t_0=0。

    代码

    注释版。应该可以看懂。

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    int n,a[25],order[25],ok=1,cir[25],cnt,st,eqcnt;
    pair<int,int> eq[25];
    int exgcd(int a,int b,int &x,int &y){
        if(!b){
            x=1,y=0;
            return a;
        }
        int g=exgcd(b,a%b,y,x);
        y-=a/b*x;
        return g;
    }
    // 核心:模拟一轮出圈,返回余数r,同时更新圈内数组cir、人数cnt、起始下标st
    int get_r(int &cnt,int &st,int target){
        // 找到target在圈内的下标pos
        int pos;
        for(int i=0;i<cnt;i++){
            if(cir[i]==target){
                pos=i;
                break;
            }
        }
        int m=cnt;
        // 计算从st开始逆时针走到pos的步数(不包含st,包含pos)
        int dist=(pos>=st?pos-st:pos+m-st);
        // 报数从1开始,所以走到target需报数dist+1
        int r=(dist+1)%m;
        if(r==0)r=m; // 余0表示报数是m的倍数,取最小正整数m
        // 删除pos位置的人,后面元素前移
        for(int i=pos;i<cnt-1;i++)
            cir[i]=cir[i+1];
        cnt--;
        // 下一轮开始报数的人是出局者左边的人
        if(pos==cnt)st=0;
        else st=pos;
        return r;
    }
    signed main(){
        ios::sync_with_stdio(false);
        cin.tie(0),cout.tie(0);
        cin>>n;
        // a[i]:编号i的小朋友第几个出圈
        for(int i=1;i<=n;i++)
            cin>>a[i],order[a[i]]=i;
        // 初始圈内排列1..n,cnt为当前人数,st为起始报数下标
        cnt=n,st=0;
        for(int i=0;i<n;i++)
            cir[i]=i+1;
        // 模拟n轮出圈,记录同余方程(r, m)
        for(int t=1;t<=n;t++){
            int oldm=cnt;                     // 当前人数即模数
            int r=get_r(cnt,st,order[t]);    // order[t]是第t个出圈者的编号
            eq[eqcnt++]={r,oldm};            // 方程K ≡ r (mod oldm)
        }
        // exCRT合并所有方程
        int ans=0,mod=1;
        for(int i=0;i<eqcnt;i++){
            int a=eq[i].first,m=eq[i].second;
            int x,y,g=exgcd(mod,m,x,y);
            if((a-ans)%g){
                ok=0;
                break;
            }       // 无解
            int l=mod/g*m;                    // 新模数
            int t=m/g;
            x=(x*(a-ans)/g%t+t)%t;           // 求最小非负x
            ans=(ans+mod*x)%l;               // 合并解
            mod=l;
        }
        if(!ok){
            cout<<"NIE"<<'\n';
            return 0;
        }
        if(ans==0)ans=mod;              // 保证最小正整数解
        cout<<ans<<'\n';
        return 0;
    }
    
    • 0
      @ 2026-4-18 23:20:42

      Update:更正代码,修复几个锅。

      知道每个人是第几个出圈的作用不大,不如转化为出圈序列。

      不难发现,假如当前圈内剩余人数为 LL,某个人第一次报的数为 aa,这个人的所有报数均形如 a+xLa+xL。那么某个人出圈等价于 a+xL=ka+xL=k,即 k≡a(modL)k\equiv a\pmod L。

      不难发现 aa 就是上一个出圈的人到他的距离,暴力找即可。

      问题等价于解同余方程组 $\begin{cases}k\equiv a_1\pmod n\\k\equiv a_2\pmod {n-1}\\ \ldots\\k\equiv 0\pmod 1\end{cases}$。

      直接 excrt 即可,无解即某次合并时无解。

      需要注意的是最后可能会合并出 k≡0(modx)k\equiv 0\pmod x,但显然 k≥1k\ge 1,因此此时答案为模数 xx。

      #include<cstdio>
      int cq[31];
      int n,ys[31],ms[31];
      bool td[31];
      long long gcd(long long x,long long y){
      	return x%y==0?y:gcd(y,x%y);
      }
      void exgcd(long long a,long long b,long long &x,long long &y){
      	if(!b){
      		x=1;y=0;
      		return;
      	}
      	exgcd(b,a%b,x,y);
      	long long z=x;
      	x=y;
      	y=z-a/b*y;
      }
      int main(){
      	int i;
      	scanf("%d",&n);
      	int p=0,x;
      	for(i=1;i<=n;i++){
      		scanf("%d",&x);
      		cq[x]=i;
      	}//cq 存储出圈序列 
      	for(i=1;i<=n;i++){
      		int ds=0;
      		x=cq[i];
      		while(p!=x){
      			p++;if(p>n)p=1;
      			if(!td[p])ds++;
      		}
      		ms[i]=n-i+1;
      		ys[i]=ds%ms[i];
      		td[x]=1;
      	}
      	long long res=ys[1],mod=ms[1];//excrt 流程
      	for(i=2;i<=n;i++){
      		long long a=mod,b=ms[i],t=((ys[i]-res)%b+b)%b;
      		long long x,y,r=gcd(a,b);
      		if(t%r)return printf("NIE"),0;
      		a/=r;b/=r;t/=r;
      		exgcd(a,b,x,y);
      		x=x%ms[i]*t%ms[i];
      		res+=x*mod;
      		mod*=b;
      		res=(res+mod)%mod;
      	}
      	if(!res)res+=mod;
      	printf("%lld",res);
      }
      
      • 1

      信息

      ID
      4641
      时间
      1000ms
      内存
      128MiB
      难度
      10
      标签
      递交数
      2
      已通过
      1
      上传者