POJ 3735 Training little cats -- 数学

题目链接

题目大意:

有n只猫,现在进行一轮喂食操作,一轮操作包括k次,每一次有以下三种选择:

1.给第i只猫一颗食物(ai++)

2.让第i只猫吃掉现有食物(ai = 0)

3.交换第i,j两只猫的食物数量(swap(ai, aj))

然后进行m轮同样的操作,问最后每只猫的食物数量。n≤100,k≤100,m≤10⁸。

分析:

我看到网上给的做法基本都是矩阵,所以写一个不同的做法。矩阵的大方向很容易想到,因为变换次数为8次方级别,自然联想到矩阵的幂。但是实际构造矩阵并不容易,我短暂思索之后没想出来,然后换了一个思路,想出另外一个办法了。

以ai表示当前的序列,经过一轮变换(不是一次,直接考察一轮)之后,ai可能变为以下三种结果:

1.ai+k

2.aj+k

3.0

现在把变换结果构造成一个图:

情况1,2:添加点j到点i的单向边,权重为k(情况1则添加点i到自身的边)

情况3:添加点0到点i的单向边,注意n个点从1开始编号

构造出的图包括两种子图:环以及从点0出发的链

对于环,以环为周期,环中的每个点经过一个周期之后都会增加环的总权重。

对于链,很容易证明,经过ti次操作后ai为一个固定的值,这里ti表示链中每一个点i到点0的距离。

理解了这两条性质,题目就很容易了。这种做法的时间复杂度为:构造图O(k),寻找周期O(n),求结果O(n²),因些总的时间复杂度为O(n²)。而矩阵做法为O(n³log(m))。

#include <iostream>
#include <cstdio>
#define MEMSET(a, b) memset(a, b, sizeof(a)) 

using namespace std;

typedef long long ll;

const int MAX_N = 100 + 2;
int t[MAX_N];
int add[MAX_N];
int edge[MAX_N];
int wht[MAX_N];
int vis[MAX_N];

int main(int argc, char *argv[])
{
	int n, m, k, a, b;
	char ch; 
	while (scanf("%d%d%d", &n, &m, &k), n || m || k)
	{
		for (int i = 1; i <= n; i++)
		{
			edge[i] = i;
			wht[i] = 0;
		}
		while (k--)
		{
			scanf("\n%c", &ch);
			if (ch == 'g')
			{
				scanf("%d", &a);
				wht[a]++;
			}
			else if (ch == 's')
			{
				scanf("%d%d", &a, &b);
				swap(edge[a], edge[b]);
				swap(wht[a], wht[b]);
			}
			else
			{
				scanf("%d", &a);
				edge[a] = 0;
				wht[a] = 0;
			}
		}
		MEMSET(vis, 0);
		for (int i = 1; i <= n; i++)
		{
			if (!vis[i])
			{
				int pre = edge[i];
				add[i] = wht[i];
				t[i] = 1;
				while (pre && pre != i)
				{
					vis[pre] = true;
					add[i] += wht[pre];
					pre = edge[pre];
					t[i]++;
				}
				if (pre == i)
				{
					pre = edge[i];
					while (pre != i)
					{
						t[pre] = t[i];
						add[pre] = add[i];
						pre = edge[pre];
					}
				}
				else
				{
					t[i] = -t[i];
					int lst = i;
					pre = edge[i];
					while (pre)
					{
						t[pre] = t[lst] - 1;
						add[pre] = add[lst] - wht[lst];
						lst = pre;
						pre = edge[pre];
					}
				}
			}
		}
		for (int i = 1; i <= n; i++)
		{
			ll ans;
			if (t[i] > 0)
			{
				ans = m / t[i] * (ll)add[i];
				int cnt = m % t[i], pos = i;
				while (cnt--)
				{
					ans += wht[pos];
					pos = edge[pos];
				}
			}
			else
			{
				t[i] = -t[i];
				if (m >= t[i]) ans = add[i];
				else
				{
					ans = 0;
					int cnt = m, pos = i;
					while (cnt--)
					{
						ans += wht[pos];
						pos = edge[pos];
					}
				}
			}
			if (i < n) printf("%lld ", ans);
			else printf("%lld\n", ans);
		}
	}
	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值