Codeforces Round #196 contest/338 div1

problem A. Quiz

YY一下,贪心就行。

对于F[N] = 2*( F[N - 1] + K)的计算,可以:

1、推出等比数列公式,然后用整数快速幂。

2、矩阵快速幂计算

3、F[N] + 2*K = 2 * (F[N - 1] + 2*K)。设a[N] = F[N] + 2*K,转换递归式,然后整数快速幂。

都可以求解。

#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
using namespace std;
#define llg long long
template<typename T> inline void checkMin(T& a, T b) { if (a > b) a = b; }
template<typename T> inline void checkMax(T& a, T b) { if (a < b) a = b; }
template<class T> inline T Min(T x,T y){return (x>y?y:x);} 
template<class T> inline T Max(T x,T y){return (x<y?y:x);}
llg mod = 1000000009;
llg n,m,k;
llg ans;
llg quickpow(llg a,llg b)
{
	llg ans = 1;
	while(b)
	{
		if(b&1)	ans = (ans * a)%mod;
		a = (a * a)%mod;
		b = b>>1;
	}
	return ans;
}
int main()
{
	cin>>n>>m>>k;
	ans = 0;
	llg wa = n - m;
	llg r = (n - m) * k;
	if(r >= n)
	  ans = m;
	else
	{
		llg nn = n - r;
		llg a = nn / k;
		llg b = nn % k;
		ans = ((quickpow(2,a + 1) - 2 + mod)%mod)*k%mod;
		ans += b + wa*(k - 1)%mod;
		ans %= mod;
	}
	cout<<ans<<endl;
	return 0;
}


problem B. Book of Evil

赛后才过的,很明显的树形DP。

推荐可以先做弱化版本hdu 2196。

思想关键在于对dpdown,dpup的理解。

第一个dfs1:父亲问儿子们:儿子们,从你们往下走,碰到最远的evil有多远啊?从各个儿子回答中,得出dpdown[ father ][ 0 ]即自己距离evil最远的距离,dpdown[ father ][ 1]即自己距离evil次最远的距离。

第二个dfs2:儿子跟父亲说:父亲,我往你这边走,可以到达evil的最远距离是多少啊?更新dpup[i]。


#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
#define maxn 100010
struct tree
{
	vector<int> child;
}node[maxn];
int n,m,d;
int dpdown[maxn][2];/*0 is longest 1 is second*/
int dpup[maxn];
int next[maxn];/*longest distance next*/
bool vis[maxn];
bool evil[maxn];
void addedge(int x,int y)
{
	node[x].child.push_back(y);
	node[y].child.push_back(x);
}
int mymax(int x,int y)
{return x>y?x:y;}

void dfs1(int fa,int now)
{
	vis[now] = true;
	dpdown[now][0] = dpdown[now][1] = -1;
	dpup[now] = -1;
	next[now] = 0;

	if(evil[now])	dpdown[now][0] = 0;
	for(int i = 0;i < node[now].child.size();i++)
	{
		int son = node[now].child[i];
		if(vis[son] || son == fa)	continue;
		dfs1(now,son);
		
		if(dpdown[son][0] >= 0)
		{/*除去-1*/
			if(dpdown[son][0] + 1 >= dpdown[now][0])
			{
				dpdown[now][1] = mymax(dpdown[now][0],dpdown[now][1]);

				dpdown[now][0] = dpdown[son][0] + 1;
				next[now] = son;
			}
			else
			{
				dpdown[now][1] = mymax(dpdown[son][0] + 1,dpdown[now][1]);
			}
		}
	}
}
void dfs2(int fa,int now)
{
	for(int i = 0;i < node[now].child.size();i++)
	{
		int son = node[now].child[i];
		if(son == fa)	continue;
		
		if(dpup[now] >= 0)
			dpup[son] = dpup[now] + 1;
		if(next[now] != son && dpdown[now][0] >= 0)
		{
			dpup[son] = mymax(dpup[son],dpdown[now][0] + 1);
		}
		else if(dpdown[now][1] >= 0)
		{
			dpup[son] = mymax(dpup[son],dpdown[now][1] + 1);
		}
		dfs2(now,son);
	}
}

int main()
{
	scanf("%d %d %d",&n,&m,&d);
	for(int i = 1;i <= m;i++)
	{
		int x;
		scanf("%d",&x);
		evil[x] = true;
	}
	for(int i = 1;i <= n - 1;i++)
	{
		int x,y;
		scanf("%d %d",&x,&y);
		addedge(x,y);
	}
	dfs1(-1,1);
	dfs2(-1,1);
	int ans = 0;
	for(int i = 1;i <= n;i++)
	{
		if(dpdown[i][0] <= d && dpup[i] <= d)
		  ans++;
	}
	printf("%d\n",ans);
	return 0;
}

problem C. Divisor Tree

暴力枚举树。。

每个数字有这样几个去向:

1、a1作为a2的一个因子,即“有father的数字"这一类型。

2、a1不作为任何数字的因子,那么可以将这些类型的数字:一、素因子个数直接加到答案,二、最后合并为根,一个就不需要合并了。


#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
#include <limits.h>
using namespace std;
#define llg long long
template<typename T> inline void checkMin(T& a, T b) { if (a > b) a = b; }
template<typename T> inline void checkMax(T& a, T b) { if (a < b) a = b; }
template<class T> inline T Min(T x,T y){return (x>y?y:x);} 
template<class T> inline T Max(T x,T y){return (x<y?y:x);}
#define maxn 20
llg a[maxn],ta[maxn];
int fa[maxn],cnt[maxn];
int n;
int ans = INT_MAX;
void ok()
{
	int twotree = 0;
	int tans = 0;
	for(int i = 1;i <= n;i++)
	{
		if(fa[i] == 0)
		{
			twotree++;
			tans += cnt[i];
		}
		if(cnt[i] == 1)/*不要算自己*/
		  tans--;
	}
	tans += n;
	if(twotree >= 2)	tans++;
	if(tans < ans)		ans = tans;
}
void dfs(int now)
{
	if(now == n)	{ok();return ;}
	for(int i = now+1;i <= n;i++)
	{
		if(ta[i]%a[now] == 0)
		{/*被用来当作因子*/
			ta[i] /= a[now];
			fa[now] = i;
			dfs(now + 1);
			ta[i] *= a[now];
		}
	}
	fa[now] = 0;//不被任何当作因子,即最后多棵树合并
	dfs(now + 1);
}

int main()
{
	cin>>n;
	for(int i = 1;i <= n;i++)
	{
		cin>>a[i];
	}
	sort(a+1,a+1+n);
	for(int i = 1;i <= n;i++)
	  ta[i] = a[i];
	for(int i = 1;i <= n;i++)
	{
		llg tmp = a[i];
		for(llg k = 2;k*k <= a[i];k++)
		  while(tmp%k == 0)
		  {
			  cnt[i]++;
			  tmp /= k;
		  }
		if(tmp > 1)	cnt[i]++;
	}
	dfs(1);
	cout<<ans<<endl;
	return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值