Codeforces Round #681 (Div. 2)E題解

chitudexixi發表於2020-11-11

連結

http://codeforces.com/contest/1443/problem/E

題意:
給你一個長度為n的排序[1,2,3,4…,n],你現在有2種操作:
1.對某個區間[l,r]求和。
2.將排序依次往後遞推x次按字典序。
(q,n<=2e5,x<=1e5)
思路:
xq<=1e52e5=2e10次,所以14的階乘>2e10次,所以只用計算前14位的位置變換即可,用逆康託展開暴力更新前14位位置,然後維護字首和。
不知道逆康託展開的同學點這裡:逆康託展開
程式碼:

#include<iostream>
#include<unordered_set>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<math.h>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#define ll long long
using namespace std;
const int N=2e5+10;
int n,q;
ll fac[20];
ll sum[N];
ll cnt;
void init()
{
	for(int i=1;i<=n;i++)
		sum[i]=sum[i-1]+i;
	fac[0]=1;
	for(int i=1;i<=17;i++)
		fac[i]=fac[i-1]*i;
}
vector<int> rev_kangtuo(int n,ll k)
{
	k++;
	vector<int> vec;
	vector<int> res;
	for(int i=0;i<n;i++) vec.push_back(i+1);
	if(k==1) return vec;
	ll base=fac[n-1];
	k--;
	for(int i=0;i<n-1;i++)
	{
		ll divres=k/base;
		k%=base;
		int num=vec[divres];
		res.push_back(num);
		vec.erase(vec.begin()+divres);
		base=fac[n-2-i];
	}
	res.push_back(vec[0]);
	return res;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
    //freopen("test.in","r",stdin);
    //freopen("test.out","w",stdout);
	scanf("%d%d",&n,&q);
	init();
	while(q--)
	{
		int x;
		scanf("%d",&x);
		int R=max(1,n-15);
		if(x==1)
		{
			int l,r;
			scanf("%d%d",&l,&r);
			if(r<R) printf("%lld\n",sum[r]-sum[l-1]);
			else 
			{
				ll ans=0;
				int start;
				if(l<R)
				{
					ans+=sum[R-1]-sum[l-1];
					start=R;
				}
				else start=l;
				vector<int> v;
				v=rev_kangtuo(n-(R-1),cnt);
				for(int i=start-R;i<=r-R;i++) ans+=v[i]+(R-1);
				printf("%lld\n",ans);
			}
		}
		else
		{
			int x;scanf("%d",&x);
			cnt+=x;
		}
	}
	return 0;
}

相關文章