3 条题解

  • 4
    @ 2023-12-29 15:14:30

    首先我们考虑一个4040分的暴力做法:直接模拟TT轮,这个非常好写,复杂度O(nT)O(nT)

    另外有2020分是T106T\leq 10^6,我们考虑到,最多只关心Q=10Q=10个位置的答案,完全可以考虑对这1010个位置来模拟。

    如果一个元素当前在第xx个位置,那么,如果xx是奇数,则会在下一轮走到第x/2\lceil x/2 \rceil个位置上,如果xx是偶数,则会在下一轮走到第n/2+x/2\lceil n/2 \rceil+x/2个位置上。那么我们显然可以在O(QT)O(QT)的时间得到答案。

    另外还有2020分是n1000n\leq 1000,我们尝试用一开始的暴力算法去模拟,会发现一个重要的结论:最多经过不超过nn的轮数,整个序列就又回到了初始时候的[1,2,3,...][1,2,3,...]

    那么,我们先模拟一下记录周期PP,用T%PT\%P,然后直接模拟T%PT\%P轮即可。

    刚才的暴力做法给了我们很大的提示:其实位置之间是构成了一些环的。

    比如n=10n=10的时候,位置33会在每一轮变化到位置3,2,6,8,9,5,3,2,6,...3,2,6,8,9,5,3,2,6,...这些位置,我们直接把326893-2-6-8-9这个环找到,就可以随便怎样算出来任意一个数字经过任意轮数所对应的答案了。(这个做法在Q=nQ=n,每次询问的TT不一样的时候一样可以解决)。

    当然,也可以算一下所有环环长的最小公倍数LL,那么所有的数字都有LL这么大的周期,直接用T106T\leq 10^6的算法那模拟TT%L轮即可。

    #include<bits/stdc++.h>
    #define ll long long
    #define maxn 1000005
    #define mod 998244353
    using namespace std;
    int n,Q;
    ll T;
    int vis[maxn];
    int main()
    {
    	cin>>n;
    	int zong=0;
    	int lcm=1;
    	for (int i=2;i<=n;i++)
    	if (!vis[i])
    	{
    		vis[i]=1;
    		int now=i,cnt=0;
    		while (true)
    		{
    			cnt++;
    			if (now&1) now=(now+1)/2; else now=(n+1)/2+now/2;
    			if (now==i) break;	
    			vis[now]=1;
    		}	
    		lcm=lcm/__gcd(lcm,cnt)*cnt;
    	}
    	ll T,Q;
    	cin>>T>>Q; T%=lcm;
    	int now;
    	while (Q--)
    	{
    		cin>>now;
    		for (int i=1;i<=T;i++)
    		{
    			if (now&1) now=(now+1)/2; else now=(n+1)/2+now/2;
    		}	
    		cout<<now<<" ";
    	}
    	cout<<endl;
    }
    
    • 2
      @ 2023-12-29 16:45:12

      补一个 Q106Q\le 10^6 数据的代码。顺便还附带了打表的程序片段。

      #include <bits/stdc++.h>
      #define int long long
      using namespace std;
      const int MAXN = 1000000;
      int n, T, Q;
      // 打表看看
      namespace TEST
      {
          vector<int> a, b;
          void out()
          {
              int tim = 1;
              for (int i = 1; i <= n; i++)
                  a.push_back(i);
              cout << tim++ << ": ";
              for (int x : a)
                  cout << x << " ";
              cout << "\n";
              while (T--)
              {
                  b.clear();
                  for (int i = 0; i < n; i += 2)
                      b.push_back(a[i]);
                  for (int i = 1; i < n; i += 2)
                      b.push_back(a[i]);
                  a = b;
                  cout << tim++ << ": ";
                  for (int x : a)
                      cout << x << " ";
                  cout << "\n";
              }
          }
      }
      int nxt[MAXN + 5];
      bool vis[MAXN + 5];
      vector<int> e[MAXN + 5];
      int ans[MAXN + 5];
      signed main()
      {
          ios::sync_with_stdio(false);
          cin.tie(0);
          cin >> n >> T >> Q;
          // TEST::out();
          int base = n / 2 + (n & 1); // 奇数数量
          for (int i = 1; i <= n; i++)
          {
              if (i % 2 == 1)
                  nxt[i] = 1 + i / 2;
              else
                  nxt[i] = base + i / 2;
          }
          // 每个点入度出度都是 1,所以必然是形成了几个环,现在把每个环都找到存下来
          // 存成 vector 之后方便一口气做一个 T 轮的轮换
          for (int i = 1; i <= n; i++)
          {
              int now = i;
              while (!vis[now])
              {
                  vis[now] = true;
                  e[i].push_back(now);
                  now = nxt[now];
              }
          }
          // getAns
          for (int i = 1; i <= n; i++)
          {
              if (e[i].empty())
                  continue;
              int siz = e[i].size();
              for (int j = 0; j < e[i].size(); j++)
                  ans[e[i][j]] = e[i][(j + T) % siz];
          }
          // 输出 Q 次询问的答案
          while (Q--)
          {
              int b;
              cin >> b;
              cout << ans[b] << " ";
          }
          return 0;
      }
      
      • 0
        @ 2023-12-31 9:30:46

        因为本题Q <= 10,所以可以考虑对于每一次查询,记录初始位置,如果下一次查询的结果=已知初始位置,则找出循环节 下面是本人的代码,可能有多余,但已经AC

        #include<bits/stdc++.h>

        using namespace std;

        const long long maxq = 100, maxn = 1e6 + 5; long long n, t, q, a[maxq], flag, sum[maxn], cnt, b, ans[maxn];

        int main() { long long i, j, num = 1;

        cin >> n >> t >> q;
        for(i = 1; i <= q; i++)
        {
        	cin >> a[i];
        }
        for(i = 1; i <= q; i++)
        {
        	j = a[i];
        	b = t;
        	cnt = 0;
        	memset(sum, 0, sizeof(sum));
        	memset(ans, 0, sizeof(ans));
        	num = 1;
        	while(1)
        	{
        		ans[num] = j;
        		num++;
        		sum[j]++;
        		if(sum[j] == 2)
        		{
        			break;
        		}
        		else
        		{
        			cnt++;
        		}
        		if(j % 2 == 0)
        		{
        			j = j / 2 + (n + 1) / 2;
        		}
        		else
        		{
        			j = j / 2 + 1;
        		}
        	}
        	b = b % cnt + 1;
        	cout << ans[b] << " ";
        }
        return 0;
        

        }

        • 1

        信息

        ID
        1384
        时间
        1000ms
        内存
        512MiB
        难度
        9
        标签
        (无)
        递交数
        234
        已通过
        24
        上传者