[POI2008]PER

很有思维的一道题

这个题的题面非常简单,出题人很友好,没有搞什么奇怪的背景,(卡农(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();}
上一篇:G.8262 SyncE conformance testing


下一篇:一文解读浅拷贝/深拷贝 (转)