FOJ有奖月赛-2014年11月 题解

Problem A: Yellowstar的第一道题

写个暴力程序会发现若n*r*c 是偶数,则是必败态,输出0.000000

否则对于3*3*3 赢的位置有:

1 0 1
0 1 0
1 0 1
0 1 0
1 0 1
0 1 0
1 0 1
0 1 0
1 0 1

1为必胜点。也就是说左上角是1,这样扩散出去。

答案就是所有1位置的概率和。

题解:点击打开链接

Problem B easy problem

因为k很小

公式: ((dep[y]-dep[x])%k+1)*val 

当确定depx后每隔k个深度增加的点权就是一个定值。

思路:

先dfs得到dfs序,然后建k个线段树,把(dep[i] - dep[1] )%k相同的放到一个线段树内操作。

当然我们不需要把点分开处理,因为只要查询时定位好点所在的线段树就可以了。

#include <cstdio>
#include <cstring>
#include <vector>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
#define lson l, mid, rt<<1
#define rson mid+1,r, rt<<1|1
typedef long long ll;
const int N =50000+10;
const int M = N+N;
//
template <class T>
inline bool rd(T &ret) {
	char c; int sgn;
	if(c=getchar(),c==EOF) return 0;
	while(c!='-'&&(c<'0'||c>'9')) c=getchar();
	sgn=(c=='-')?-1:1;
	ret=(c=='-')?0:(c-'0');
	while(c=getchar(),c>='0'&&c<='9') ret=ret*10+(c-'0');
	ret*=sgn;
	return 1;
}
template <class T>
inline void pt(T x) {
    if (x <0) {
        putchar('-');
        x = -x;
    }
    if(x>9) pt(x/10);
    putchar(x%10+'0');
}
//
struct Edge {
	int v, nex;
	Edge() {
	}
	Edge(int _v, int _nex) {
		v = _v; nex = _nex;
	}
};
struct node {
	ll v, addv;
};
struct Seg {
	node a[N<<2];
	void build(int l, int r, int rt) {
		a[rt].v = a[rt].addv = 0;
		if (l != r) {
			int mid = (l+r)>>1;
			build(lson); build(rson);
		}
	}
	void Down(node& fa, node& ls, node& rs) {
		if (fa.addv!=0) {
			ls.addv += fa.addv;
			ls.v += fa.addv;
			rs.addv += fa.addv;
			rs.v += fa.addv;
			fa.addv = 0;
		}
	}

	void update(int L, int R, ll v, int l, int r, int rt) {
		if (L<=l && r<=R) {
			a[rt].addv += v;
			a[rt].v += v;
		} else {
			Down(a[rt], a[rt<<1], a[rt<<1|1]);
			int mid = (l+r)>>1;
			if (R<=mid)
				update(L, R, v,lson);
			else if (L>mid)
				update(L, R, v,rson);
			else {
				update(L, mid, v, lson);
				update(mid+1, R, v, rson);
			}
		}
	}
	void query(int pos, int l, int r, int rt) {
		if (l == r) {
			//putchar('-');
			pt(a[rt].v);
			putchar('\n');
		} else {
			Down(a[rt], a[rt<<1], a[rt<<1|1]);
			int mid = (l+r)>>1;
			if (pos<=mid)
				query(pos, lson);
			else
				query(pos, rson);
		}
	}
}sol[5];

int T = 0, n, K, d[N];
int dep, pos[N], son[N];
int g[N], idx;
Edge e[M];

void add(int u, int v) {
	e[idx] = Edge(v, g[u]);
	g[u] = idx++;
}
void dfs(int v, int u, int fa) {
	++ dep;
	pos[u] = dep;
	son[u] = 1;
	d[u] = v%K;
	for (int i = g[u]; ~i; i = e[i].nex)
		if (e[i].v!=fa) {
			dfs(v+1, e[i].v, u);
			son[u] += son[e[i].v];
		}
}
int sub(int x, int y) {
	return ((y-x)%K+K)%K;
}
void work() {
	int m, typ, x, u, v;
	rd(n); rd(m); rd(K);
	idx = 0;
	memset(g, -1, sizeof(int)*(n+2));
	for (int i = 0; i < n-1; ++i) {
		rd(u); rd(v);
		add(u, v); add(v, u);
	}
	dep = 0;
	dfs(0, 1, -1);
	for (int i = 0; i < K; ++i)
		sol[i].build(1, n, 1);
	printf("Case#%d:\n", ++T);
	while (m-->0) {
		scanf("%d", &typ);
		if (typ == 1) {
			rd(u); rd(v);
			x = d[u];
			for (int j = 0; j < K; ++j)
				sol[j].update(pos[u], pos[u]+son[u]-1, (ll)(sub(x,j)+1)*v, 1, n, 1);
		} else {
			rd(u);
			sol[d[u]].query(pos[u], 1, n, 1);
		}
	}
}
int main() {
	int cas;
	scanf("%d", &cas);
	while (cas-->0) {
		work();
	}
	return 0;
}


Problem C ytaaa

思路:

n^2的dp dp[i]表示前i个数的最大价值

dp[i] = max(dp[j] + (a[i]-a[j])*(a[i]-a[j]));


Problem D 礼物分配

思路:

我们先把礼物平均分成2堆。

然后枚举1-(n/2)的所有二进制状态。0就给a,1就给b。

我们设此时a获得的价值和为A,b获得的价值和为B

那么对于一个状态我们就能得到 B-A。

给所有B-A排个序,得到数组K

对于另一半(n/2+1) -  n也这样枚举得到A-B.然后去K数组中二分查找距离-(A-B)最近的数即可。



Problem E chriswho

记忆化搜索,从个位开始构造

Lcm(1-9) = 2520

Codeforces 55D : 点击打开链接

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 105;
const int M = 3005;//LCM(1..9) = 2520;
int a[N], num[M], cnt;
ll dp[N][N][M];//dp[i][j][k]表示考虑后i位的lcm是j,和为k的数的个数
int gcd(int a, int b) {
	while(a>0 && b>0) {
		if(a > b) a = a%b;
		else b = b%a;
	}
	return a+b;
}
void pre() {
	cnt = 0;
	for(int i = 1; i <= 2520; i ++) {
		if(2520 % i == 0) {
			num[i] = cnt++;
		}
	}
	memset(dp, -1, sizeof dp);
}
ll dfs(int len, int lcm, int sum, bool f) {
	if(len == 0) {
		if(sum%lcm == 0) return 1;
		else return 0;
	}

	if(!f && dp[len][num[lcm]][sum] != -1) {
		return dp[len][num[lcm]][sum];
	}

	ll ans = 0;
	int Max = f?a[len]:9;
	for(int i = 0; i <= Max; i ++) {

		int nlcm;
		if(i == 0) nlcm = lcm;
		else nlcm = lcm/__gcd(lcm, i)*i;

		ans += dfs(len-1, nlcm, (sum *10 + i) % 2520, f && i == Max);
	}

	if(!f) dp[len][num[lcm]][sum] = ans;
	return ans;
}
ll cal(ll n) {
	int size = 0;
	while(n > 0) {
		a[++size] = n%10;
		n /= 10;
	}
	ll ans = dfs(size, 1, 0, true);
	return ans;
}
int main() {
	pre();
	int T; scanf("%d", &T);
	while(T--) {
		ll b;
		cin>>b;
		cout<<cal(b) - 1<<endl;
	}
}


Problem F 骑士

搜索+剪枝

题解:点击打开链接

Problem G 快来买肉松饼


圆桌骑士。poj 2942.白书上的原题。。最后答案改成 n-ans即可


评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值