很有思维的一道题
这个题的题面非常简单,出题人很友好,没有搞什么奇怪的背景,(卡农(P3214)的作者看看人家),所以理解题面就是:
一句话题意:
给定一个长度为 \(n\) 的数列,求这个数列是在其全排列中的排名是多少,输出排名 \(mod\) \(m\) 的结果。
赵小兵同学:这不就是个康托展开嘛,看我A掉这个大水题。
于是,他打出了下面这个程序:
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x7f7f7f7f7f
const int one = 1e7+10;
inline void openfile()
{freopen("t.txt","r",stdin);}
namespace zxb
{
int tree[1000005];
int n;
inline int lowbit(int x)
{
return x&-x;
}
inline void update(int x,int y)
{
while(x<=n){
tree[x]+=y;
x+=lowbit(x);
}
}
inline int query(int x)
{
int sum=0;
while(x)
{
sum+=tree[x];
x-=lowbit(x);
}
return sum;
}
int mod;
int jc[1000005];
int a[1000005];
inline short main()
{
//openfile();
jc[0] = jc[1] = 1;
cin >> n >> mod;
for(int i=1;i<=n;i++)
{
jc[i]=(jc[i-1]*i)%mod;
update(i,1);
}
int ans = 0;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
ans=(ans+((query(a[i])-1)*jc[n-i]) % mod) % mod;
update(a[i],-1);
}
cout<<ans +1<<endl;
return 0;
}
}
signed main() { return zxb::main();}
然后。。。
赵小兵同学翻车记录
旁边的 \(C\) 君看不下去了,转眼就打了一个暴力算法,并对赵小兵同学发来了鄙视
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x7f7f7f7f7f
const int one = 1e7+10;
inline void openfile()
{freopen("t.txt","r",stdin);}
inline int get()
{
int s = 0,f = 1;
register char ch = getchar();
while(!isdigit(ch))
{
if(ch == '-') f= -1;
ch = getchar();
}
while(isdigit(ch))
{
s = s* 10 + ch - '0';
ch = getchar();
}
return s * f;
}
namespace xin
{
int a[one],b[one];
int mod,n;
inline bool comp()
{
for(register int i=1;i<=n;++i)
if(a[i] != b[i])
return false;
return true;
}
inline short main()
{
// openfile();
n = get(); mod = get();
for(register int i=1;i<=n;++i) a[i] = get(),b[i] = a[i];
sort(a+1,a+1+n);
int cnt = 1;
while(next_permutation(a+1,a+n+1))
{
cnt++;
if(comp() == 1)
{
cout<<cnt % mod<<endl;
return 0;
}
}
return 0;
}
}
signed main() { return xin::main();}
\(C\) 君的暴力记录\(C\) 君的暴力记录
\(C\) 君整整比赵小兵同学多了一倍的分数,这时候,坐在一旁的 \(XIN\) 同学看不下去了,但 \(XIN\) 同学一时间也无法想出正解,但是经过 \(XIN\) 同学为期 \(3\) 天的不懈奋斗,终于 \(A\) 掉了这个题目:
赵小冰和 \(C\) 君都想要知道他的思路, \(XIN\) 同学开始讲到:
首先,要考虑每一个数位的贡献值,其实赵小兵同学的第一想法是不错的,但是只能说是不对,因为这个题目存在重复,也存在模数为合数的时候,所以康托展开就不管用了,所以要考虑别的想法,发现全排列就和本身的字典序有关,之后得出计算式:
\[w \cdot \frac{(n-i)!}{\Pi \ {cnt[j]!}} \]$cnt $ 就是从 \(i\) 到 $n $ 中 \(j\) 所出现的个数
但是问题就又来了,合数应该怎么处理呢???
最先考虑费马小定理:
\[a^{p-1}≡1 (mod\ p) \]可是,模数为合数啊,所以考虑扩展欧几里的求出逆元:
inline void exgcd(int a,int b,int &x,int &y)
{
if(!b) { x = 1; y = 0; return;}
exgcd(b,a%b,y,x);
y -= a / b * x;
}
inline int get_inv(int x,int p)
{
int a,b;
exgcd(x,p,a,b);
return (a % p + p) % p;
}
之后可以用树状数组在 \(log\) 的时间复杂度的情况下求出区间最小值:
namespace xin_bit
{
int s[maxn];
inline int lowbit(int x)
{return x & -x;}
inline void add(int x,int val)
{
while(x <= maxn)
{
s[x] += val;
x += lowbit(x);
}
}
inline int query(int x)
{
register int ret = 0;
while(x)
{
ret += s[x];
x -= lowbit(x);
}
return ret;
}
}
之后来一点小小的转移,将因子分解,之后再合并,最后就可以按照计算式求出本题的答案了。
慷慨的 \(XIN\) 还放出了代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 1e6+10;
namespace xin_io
{
inline int get()
{
int s =0 ,f = 1;
register char ch = getchar();
while(!isdigit(ch))
{
if(ch == '-') f= -1;
ch = getchar();
}
while(isdigit(ch))
{
s = s *10 + ch - '0';
ch = getchar();
}
return s * f;
}
inline void write(int x)
{
if(x < 0) putchar('-'),x = -x;
if(x > 9) write(x/10);
putchar(x % 10 + '0');
}
}
using xin_io::get; using xin_io::write;
inline void openfile()
{freopen("t.txt","r",stdin);}
namespace xin_bit
{
int s[maxn];
inline int lowbit(int x)
{return x & -x;}
inline void add(int x,int val)
{
while(x <= maxn)
{
s[x] += val;
x += lowbit(x);
}
}
inline int query(int x)
{
register int ret = 0;
while(x)
{
ret += s[x];
x -= lowbit(x);
}
return ret;
}
}
namespace xin
{
#define m(c) cout<<sizeof(c) / (1 << 20) << "MB"<<endl
#define min(a,b) (a) < (b) ? (a) : (b)
#define max(a,b) (a) > (b) ? (a) : (b)
int mod;
int n,m;
bool number[maxn];
int prime[maxn];
int i,j,count=0;
inline void xin_shai(int N)
{
memset(number,true,sizeof(number));
for(i=2;i<=N;i++)
{
if(number[i])
prime[count++]=i;
for(j=0;j<count and prime[j]*i<=N;j++)
{
number[prime[j]*i]=false;
if(i%prime[j]==0)
break;
}
}
}
inline void exgcd(int a,int b,int &x,int &y)
{
if(!b) { x = 1; y = 0; return;}
exgcd(b,a%b,y,x);
y -= a / b * x;
}
inline int get_inv(int x,int p)
{
int a,b;
exgcd(x,p,a,b);
return (a % p + p) % p;
}
int cnt[110],link[110][maxn];
int rec1[maxn],rec2[maxn];
int temp,a[maxn],fac[maxn],num_fac = 0;
inline void cont(int &x,int val)
{
for(register int i=1;i<=num_fac;++i)
{
register int zhuan = fac[i];
while(!(x % zhuan))
x/=zhuan,cnt[i] += val;
}
}
inline int get_num()
{
int ret = 1;
for(register int i=1;i<=num_fac;++i)
ret = (ret * link[i][min(rec2[i],cnt[i])] ) % m;
return ret;
}
inline int work()
{
int ans = 0,ret = 1;
rec1[a[n]] = 1; xin_bit::add(a[n],1);
for(register int i=n-1;i>=1;--i)
{
int zhuan = n - i;
cont(zhuan,1);
ret *= zhuan; ret %= m;
zhuan = ++rec1[a[i]];
cont(zhuan,-1);
ret = (ret * get_inv(zhuan,m) + m) % m;
xin_bit::add(a[i],1);
zhuan = get_num();
ans += (ret % m * xin_bit::query(a[i] - 1) % m * zhuan % m + m ) % m;
ans %= m;
}
return ++ans;
}
inline short main()
{
openfile();
n = get(); m = get(); temp = m;
for(register int i=1;i<=n;++i) a[i] = get();
for(register int i=2;i<=sqrt(temp);++i)
if(temp % i == 0)
{
fac[++num_fac] = i;
link[num_fac][0] = 1;
for(register int j=1;j<=maxn;++j)
link[num_fac][j] = link[num_fac][j-1] * i % m,rec2[num_fac] ++;
while(temp % i == 0)temp /= i;
}
if(temp > 1)
{
fac[++num_fac] = temp;
link[num_fac][0] = 1;
for(register int j=1;j<=maxn;++j)
link[num_fac][j] = link[num_fac][j-1] * temp % m,rec2[num_fac]++;
}
int ans = work() % m;
write(ans);
putchar('\n');
return 0;
}
}
signed main() {return xin::main();}