2 条题解
-
0
废话时间
这题思维上可能算不上紫,但是考虑到 模板 exCRT 也是紫,所以评紫应该是没问题。
正文
首先让我们来观察一下本题,第 轮报数的时候,不难发现当圈中还剩下 个人,我们记 。如果第 个人报了 ,那么这个人报的数就一定是 ,其中 是整数。
那对于第 轮出局的人 ,我们记这个人报了 ,那么必有 。
显然对于每一轮都能产生一个同余方程,那么我们就能获得一个方程个数为 的同余方程组:
$$\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 求解,方程组无解则是问题无解。
接下来我们考虑如何求出 。其实这个就相当于当前圈起点到当前点的距离,而根据规则,当前圈的起点应该是上一圈淘汰者的后一个位置,也就是说,我们可以求出 到 的距离。特别的,我们规定 。
代码
注释版。应该可以看懂。
#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
Update:更正代码,修复几个锅。
知道每个人是第几个出圈的作用不大,不如转化为出圈序列。
不难发现,假如当前圈内剩余人数为 ,某个人第一次报的数为 ,这个人的所有报数均形如 。那么某个人出圈等价于 ,即 。
不难发现 就是上一个出圈的人到他的距离,暴力找即可。
问题等价于解同余方程组 $\begin{cases}k\equiv a_1\pmod n\\k\equiv a_2\pmod {n-1}\\ \ldots\\k\equiv 0\pmod 1\end{cases}$。
直接 excrt 即可,无解即某次合并时无解。
需要注意的是最后可能会合并出 ,但显然 ,因此此时答案为模数 。
#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
- 上传者