HDU-6038 Function - 2017 Multi-University Training Contest - Team 1(构造置换或强连通分量)

29 篇文章 0 订阅
19 篇文章 2 订阅

题意:给出两个排列,a和b,求满足映射的f[i] = bf[a[i]]的个数。

思路:

one. 列出所有式子,进行连边,一个环中某一个元素一旦确定,其它也就确定了,所以需要求强连通分量,要想能够构成一个k个元素的强连通分量,只有m个数中的连环数个数为k的因子时才能够构成。然后再对所有连通分量的方案数进行乘积。

two. 用置换群的思想,通过f函数构造一个新的置换,然后找到构造的置换和m个数置换的轮换即一个环进行操作即可。


代码1:

#include <algorithm>
#include <string.h>
#include <cstdio>
#include <map>
#define LL long long
using namespace std;
const LL mod = 1e9+7;
const int maxn = 1e5+5;
const int maxm = maxn;
struct node
{
	int v, next;
} edge[maxm];
int no, head[maxn];
int n, m;
int index, top, num[maxn], low[maxn], book[maxn], S[maxn];
int a[maxn], b[maxn];
int vis[maxn], circle[maxn];
map<int, int> mp;
map<int, int>::iterator it;
LL ans, final;
inline void init()
{
	mp.clear();
	no = 0; index = 0; top = 0;
	memset(head, -1, sizeof head);
	memset(book, 0, sizeof book);
	memset(circle, 0, sizeof circle);
	memset(num, 0, sizeof num);
	memset(vis, 0, sizeof vis);
}
inline void add(int u, int v)
{
	edge[no].v = v; edge[no].next = head[u];
	head[u] = no++;
}
void tarjan(int cur)
{
	num[cur] = low[cur] = ++index;
	vis[cur] = 1; S[++top] = cur;
	for(int k = head[cur]; k != -1; k = edge[k].next)
	{
		if(!num[edge[k].v])
		{
			tarjan(edge[k].v);
			low[cur] = min(low[cur], low[edge[k].v]);
		}
		else if(vis[edge[k].v])
		{
			low[cur] = min(low[cur], num[edge[k].v]);
		}
	}
	if(num[cur] == low[cur])
	{
		int cnt = 0;
		do
		{
			++cnt;
			vis[S[top]] = 0;
			--top;
		}
		while(S[top+1] != cur);
		++mp[cnt];
	}
}
int main()
{
	int count = 0;
	while(~scanf("%d %d", &n, &m))
	{
		init(); final = 1;
		for(int i = 0; i < n; ++i) scanf("%d", &a[i]);
		for(int i = 0; i < m; ++i) scanf("%d", &b[i]);
		for(int i = 0; i < m; ++i)
		{
			if(book[i]) continue;
			int k = b[i], cnt = 1;
			while(k != i) ++cnt, k = b[k];
			k = b[i]; book[i] = 1; ++circle[cnt];
			while(k != i) ++circle[cnt], book[k] = 1, k = b[k];
		}
		for(int i = 0; i < n; ++i) add(a[i], i);
		for(int i = 0; i < n; ++i) if(!num[i]) tarjan(i);
		for(it = mp.begin(); it != mp.end(); ++it)
		{
			int i, k = it->first, t = it->second;
			ans = 0;
			ans = (ans+(LL)circle[1])%mod;
			for(i = 2; i*i < k; ++i)
			{
				if(k%i == 0)
				{
					ans = (ans+(LL)circle[i])%mod;
					ans = (ans+(LL)circle[k/i])%mod;
				}
			}
			if(k%i==0 && k/i == i) ans = (ans+(LL)circle[i])%mod;
			if(k > 1) ans = (ans+(LL)circle[k])%mod;
			for(int j = 1; j <= t; ++j) final = final*ans%mod;
		}
		printf("Case #%d: %lld\n", ++count, final);
	}
	return 0;
}


代码2:

#include <algorithm>  
#include <string.h>  
#include <cstdio>  
#include <map>  
#define LL long long  
using namespace std;  
const LL mod = 1e9+7;  
const int maxn = 1e5+5; 
int n, m;
int a[maxn], b[maxn], gz[maxn];  
int vis[maxn], cir1[maxn];  
map<int, int> mp;  
map<int, int>::iterator it;  
LL ans, final;
int main()  
{  
    int count = 0;  
    while(~scanf("%d %d", &n, &m))  
    {   
        memset(cir1, 0, sizeof cir1);
        memset(vis, 0, sizeof vis); final = 1; 
        for(int i = 0; i < n; ++i) scanf("%d", &a[i]);  
        for(int i = 0; i < m; ++i) scanf("%d", &b[i]);  
        for(int i = 0; i < m; ++i)  
        {  
            if(vis[i]) continue;  
            int k = b[i], cnt = 1;  
            while(k != i) ++cnt, k = b[k];  
            k = b[i]; vis[i] = 1; ++cir1[cnt];  
            while(k != i) ++cir1[cnt], vis[k] = 1, k = b[k];  
        }
		memset(vis, 0, sizeof vis); mp.clear();
        for(int i = 0; i < n; ++i) gz[a[i]] = i; 
        for(int i = 0; i < n; ++i)  
        {  
            if(vis[i]) continue;  
            int k = gz[i], cnt = 1;  
            while(k != i) ++cnt, k = gz[k];  
            k = gz[i]; vis[i] = 1; 
            while(k != i) vis[k] = 1, k = gz[k]; 
			++mp[cnt];
        }
        for(it = mp.begin(); it != mp.end(); ++it)
        {  
            int i, k = it->first, t = it->second;  
            ans = 0;  
            ans = (ans+(LL)cir1[1])%mod;  
            for(i = 2; i*i < k; ++i)  
            {  
                if(k%i == 0)  
                {  
                    ans = (ans+(LL)cir1[i])%mod;  
                    ans = (ans+(LL)cir1[k/i])%mod;  
                }  
            }  
            if(k%i==0 && k/i == i) ans = (ans+(LL)cir1[i])%mod;  
            if(k > 1) ans = (ans+(LL)cir1[k])%mod;  
            for(int j = 1; j <= t; ++j) final = final*ans%mod;
        }  
        printf("Case #%d: %lld\n", ++count, final);  
    }  
    return 0;  
}

继续加油~

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值