置换,置换的运算

置换的概念还是比较好理解的,《组合数学》里面有讲。对于置换的幂运算大家可以参考一下潘震皓的那篇《置换群快速幂运算研究与探讨》,写的很好。

结论一:一个长度为 L 的循环 T,L 是 k 的倍数,则 Tk 是k个循环的乘积,每个循环分别是循环 T 中下标 i mod k= 0,1,2…的元素按顺序的连接。
结论二:一个长度为 L 的循环T,gcd(L,k)=1,则 Tk 是一个循环,与循环 T 不一定相同。
结论三:一个长度为 L 的循环T, 是gcd(L,k)个循环的乘积,每个循环分别是循环 T 中下标 i mod gcd(l,k)=0,1,2…的元素的连接。

如果长度与指数不互质,单个循环就没有办法来开方。不过,我们可以选择相应m个长度相同的循环交错合并来完成开方的过程。可在这种情况下,如果找不到m个长度相同的循环,那就一定不能开方。其中:m是gcd(l,k)的倍数

POJ 3270 Cow Sorting http://poj.org/problem?id=3270
Description

Farmer John’s N (1 ≤ N ≤ 10,000) cows are lined up to be milked in the evening. Each cow has a unique “grumpiness” level in the range 1…100,000. Since grumpy cows are more likely to damage FJ’s milking equipment, FJ would like to reorder the cows in line so they are lined up in increasing order of grumpiness. During this process, the places of any two cows (not necessarily adjacent) can be interchanged. Since grumpy cows are harder to move, it takes FJ a total of X+Y units of time to exchange two cows whose grumpiness levels are X and Y.

Please help FJ calculate the minimal time required to reorder the cows.

Input

Line 1: A single integer: N.
Lines 2…N+1: Each line contains a single integer: line i+1 describes the grumpiness of cow i.
Output

Line 1: A single line with the minimal time required to reorder the cows in increasing order of grumpiness.
Sample Input

3
2
3
1
Sample Output

7
Hint

2 3 1 : Initial order.
2 1 3 : After interchanging cows with grumpiness 3 and 1 (time=1+3=4).
1 2 3 : After interchanging cows with grumpiness 1 and 2 (time=2+1=3).

题意的话,给你一个数字序列(每个数字唯一),每次你可以交换任意两个数字,代价为这两个数字的和,问最少代价能把这个序列按升序排列好。

  1. 找问题的初状态和末状态。
  2. 画出循环群。以 8 4 5 3 2 7为例,末状态是 2 3 4 5 7 8,置换群 (2 7 8)(3 5 4)
  3. 观察其中一个循环,很明显要是代价最小,必须用该循环里最小的元素 2,与其他两个元素 7 8 做交换,最小代价为:
sum - _min + (len-1) * _min;
  也就是:
sum + (len-2) * _min;
  PS.sum 是该循环的元素之和,len 循环长度,_min 循环中的最小元素。
  1. 考虑到另外一种情况,我们可以从别的循环里面调一个数字,进入这个循环之中,使交换代价更小。例如初始状态:1 8 9 7 6,末状态 1 6 7 8 9,置换群 (1)(8 6 9 7)
    明显,第二个循环为(8 6 9 7),最小的数字为6。我们可以抽调整个数列最小的数字1进入这个循环。使第二个循环变为:(8 1 9 7)。让这个1完成任务后,再和6交换,让6重新回到循环之后。这样做的代价明显是:
sum + _min + (len + 1) * _minn
  PS._minn是整个序列的最小元素
  1. 因此,对一个循环的排序,其代价是sum + (len-2) * _min 和 sum + _min + (len + 1) * _minn之中小的那个数字。
    试想一下,所有循环的sum总和,一定是等于数列元素总和的,所以我们只需要比较 (len-2) * _min和sum + _min + (len + 1) * _minn的大小即可。
    但这里两个公式还不知道怎么推出来的。
  2. 我们在计算循环的时候,不需要记录这个循环的所有元素,只需要记录这个循环的最小的数。
  3. 在储存数目的时候,我们可以使用一个hash结构,将元素及其位置对应起来,以达到知道元素,可以快速反查元素位置的目的。这样就不必要一个个去搜索。
#include
using namespace std;
typedef long long ll;
typedef unsigned long long llu;
const int inf = 0x3f3f3f3f;
const ll lnf = 0x3f3f3f3f3f3f3f3f;
const int maxn = 100100;
int a[maxn],b[maxn],c[maxn];
bool p[maxn];
int main(void)
{
    int n;
    while(~scanf("%d",&n)){
        ll ans = 0;
        memset(p,false,sizeof(p));	//标记数组
        for(int i=0;i<n;i++){
            scanf("%d",a+i);
            ans += (ll)a[i];
            b[i] = a[i];
        }
        sort(b,b+n);
        for(int i=0;i<n;i++)
            c[b[i]] = i;
        int len,_min,st,id;
        ll ans1,ans2;
        for(int i=0;i<n;i++){
            id = i;
            st = a[i];
            _min = inf;
            len = 0;
            if(!p[i]){
                while(1){
                    p[id] = true;
                    if(_min>st) _min = st;
                    len++;
                    id = c[st];
                    st = a[id];
                    if(st==a[i])    break;
                }
                ans1 = (ll)(len-2)*_min;
                ans2 = (ll)_min + (ll)(len+1)*b[0];
                ans += min(ans1,ans2);
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

你可能感兴趣的:(数论训练)