Codeforces Round #196 (Div. 1) 题解

  (CF唯一不好的地方就是时差……不过还好没去考,考的话就等着滚回Div. 2了……)

  A - Quiz

  裸的贪心,不过要用矩阵乘法优化或者直接推通式然后快速幂。不过本傻叉做的时候脑子一片混乱,导致WA+TLE若干次,而且还做了很久(半小时)……

#include <cstdio>
const int MOD = 1000000000+9;
int ans,n,m,k;
int power(long long x,int k)
{
	int res = 1;
	for (;k;k >>= 1,x = x*x%MOD)
		if (k&1) res = 1ll*res*x%MOD;
	return res;
}
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("A.in","r",stdin);
	freopen("A.out","w",stdout);
	#endif
	scanf("%d%d%d",&n,&m,&k);
	long long i;
	// for (i = 0; 1; i += k) {
	// 	if (n - i <= m) break;
	// 	if (m >= k - 1) {
	// 		m -= (k - 1);
	// 		(ans += k - 1)%=MOD;
	// 	}else {
	// 		(ans += m)%=MOD;
	// 		printf("%d\n",ans);
	// 		return 0;
	// 	}
	// }
	i = 1ll*(n - m)*k;
	//printf("%d\n",i);
	if (i > n) {
		printf("%d\n",m);
		return 0;
	}
	ans = 1ll*(n - m)*(k - 1)%MOD;
	int ans1 = 0;
	//for (; i + k <= n; i += k) ans1 = (ans1 + k)%MOD*2%MOD;
	ans1 = (1ll*k*(power(2,(n - i)/k + 1) - 2 + MOD))%MOD;
	//printf("%d\n",ans1);
	(ans1 += MOD)%=MOD;
	i = (n -i)/k * k + i;
	(ans1 += n - i)%=MOD;
	printf("%d\n",(ans + ans1)%MOD);
}

  

  B - Book of Evil

  一开始看错题目了……其实这题很水……只要求出每个点离它自己最远的damaged的点,最后再一个一个点枚举过去,判断过去即可。

  

#include <cstdio>
#include <algorithm>
#include <cstring>
const int N = 100000 + 9;
int n,m,p[N],d,son[N],ec,fa[N];
struct Edge{int link,next;}es[N*2];
struct node{int dis,v;}f[N],g[N];
inline void addedge(const int x,const int y)
{
	es[++ec].link = y;
	es[ec].next = son[x];
	son[x] = ec;
}
inline void Addedge(const int x,const int y)
{addedge(x,y);addedge(y,x);}
inline void update(const int u,node x)
{
	if (x.dis == -1) return;
	++x.dis;
	if (f[u].dis < x.dis) g[u] = f[u],f[u] = x;
	else if (g[u].dis < x.dis) g[u] = x;
}
void dfs(const int root)
{
	static int son1[N];
	memcpy(son1,son,sizeof son);
	for (int u = root; u;) {
		if (son[u]) {
			const int v = es[son[u]].link;
			son[u] = es[son[u]].next;
			if (v == fa[u]) continue;
			fa[v] = u;
			u = v;
			continue;
		}
		update(fa[u],f[u]);
		u = fa[u];
	}
	for (int u = root; u;) {
		if (son1[u]) {
			const int v = es[son1[u]].link;
			son1[u] = es[son1[u]].next;
			if (v == fa[u]) continue;
			fa[v] = u;
			if (f[u].v == f[v].v) update(v,g[u]);
			else update(v,f[u]);
			u = v;
			continue;
		}
		u = fa[u];
	}
}
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("B.in","r",stdin);
	freopen("B.out","w",stdout);
	#endif
	scanf("%d%d%d",&n,&m,&d);
	memset(f, -1, sizeof f);
	memset(g, -1, sizeof g);
	for (int i = 1,x; i <= m; ++i) {
		scanf("%d",&x);
		f[x].dis = 0;
		f[x].v = x;
	}
	for (int i = 1,x,y; i < n; ++i) {
		scanf("%d%d",&x,&y);
		Addedge(x,y);
	}
	dfs(1);
	int ans = 0;
	for (int i = 1; i <= n; ++i) if (f[i].dis <= d) ++ans;
	printf("%d\n",ans);
}

  

  C - Divisor Tree

  这题有点意思。观察后不难得出除了root和leave以外的点都是所给的数,而root也有可能是。关键是接下来怎么弄……方法应该有很多。不过我想不出好的方法……

  于是去OrzTutorial。其实这个做法挺暴力的……是O(N!)。由于每个所给的数的father也是所给的数或root,并且比它本身来得大。于是我们可以给a[i]排序,然后枚举树的形态。注意细节处理。

 

#include <cstdio>
#include <algorithm>
const int N = 9;
int ans = 0x7fffffff,n,pnum[N],cnt;
long long a[N],root;
inline void getpnum()
{
	for (int i = 1; i <= n; ++i) {
		long long x = a[i];
		for (long long j = 2; j*j <= x; ++j) 
			for (;x % j == 0;x /= j) ++pnum[i];
		if (x != 1) ++pnum[i];
	}
}
void dfs2(const int idx,const int sum)
{
	if (idx == 0) {ans = std::min(ans,sum + n + 1);return;}
	for (int i = n; i > idx; --i) {
		if (a[i] % a[idx] == 0) {
			a[i] /= a[idx];
			dfs2(idx - 1,sum);
			a[i] *= a[idx];
		}
	}
	dfs2(idx - 1,sum + pnum[idx]);
}
bool dfs1(const int idx)
{
	if (idx == 0) {ans = pnum[n] + n;return 1;}
	for (int i = n; i > idx; --i) {
		if (a[i] % a[idx] == 0) {
			a[i] /= a[idx];
			if (dfs1(idx - 1)) {
				a[i] = a[i]*a[idx];
				return 1;
			}
			a[i] = a[i]*a[idx];
		}
	}
	return 0;
}
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("C.in","r",stdin);
	freopen("C.out","w",stdout);
	#endif
	scanf("%d",&n);
	for (int i = 1; i <= n; ++i) scanf("%I64d", a + i);
	std::sort(a + 1,a + 1 + n);
	root = a[n];
	getpnum();
	for (int i = 1; i <= n; ++i) if (pnum[i] == 1) ++cnt;
	//memcpy(back,a,sizeof a);
	if (!dfs1(n - 1))
		dfs2(n,0);
	printf("%d\n",ans - cnt);
}

    

  D - GCD Table

  这题有点坑……一开始想复杂了。其实:

  gcd(i,j) = a[idx] ===> a[idx] | i a[idx] | j

  就是这样,仅此而已。

  然后可以证明row = lcm(a[1], a[2], a[3], ... ,a[n])

  证明也很简单。

    首先lcm * x (x > 0)肯定可以。只需考虑gcd(lcm,j) != gcd(lcm * x,j)的情况。

    令gcd(lcm * x,j) = d。则d | j, d 不整除 lcm。

    若这个位置是合法的话,则有a[i] = d 不整除 lcm。这明显是矛盾的。

  对于col:

    col = - k + 1 (mod a[k]) 

  然后解线性同余方程组,对答案check即可。

  Postscript: 注意乘法的地方,会爆long long。所以用类似快速幂的方法处理。

  附图一张(描述了大家被这个trick坑的惨状……)

  

  

//greater than max_long_long ... so use quick_multiply (qmult)!
#include <cstdio>
#include <cmath>
const int N = 10000 + 9;
typedef long long ll;
ll n,m,a[N],K;
struct Triple
{
	ll x,y,z;
	Triple(const ll _x,const ll _y,const ll _z):
		x(_x),y(_y),z(_z){}
};
Triple exgcd(const ll a,const ll b)
{
	//  Ax1 + By1 = ax  + by
	//  A = b, B = a % b
	//  b * x1 + (a - a/b*b) * y1 = a * x + b * y
	//  a*y1 + b*(x1 - a/b*y1) = a*x + b*y
	//  x = y1, y = x1 - a/b*y1
	if (!b) return Triple(1,0,a);
	Triple last(exgcd(b,a%b));
	return Triple(last.y,last.x - a/b * last.y,last.z);
}
ll gcd(ll a,ll b)
{
	for (ll t;b;)
		t = a,a = b,b = t % b;
	return a;
}
bool calc_row()
{
	ll last = 1;
	for (int i = 1; i <= K; ++i) {
		ll tmp = gcd(last,a[i]);
		if (last / tmp > n / a[i]) return false;
		last = last / tmp * a[i];
	}
	return true;
}
ll qmult(ll x,ll k,const ll mod)
{
	ll res = 0,t = 1;
	if (k < 0) t = -1,k = -k;
	for (;k;k >>= 1,x = x*2%mod)
		if (k&1) res = (res + x) % mod;
	return res*t;
}
bool calc_col()
{
	// j = -k (mod a[k + 1])
	// x = k1 (mod a1) <=> x = a1*p1 + k1
	// x = k2 (mod a2) <=> x = a2*p2 + k2
	// x = a1*p1 + k1 = a2*p2 + k2 <=> a1*p1 - a2*p2 = k2 - k1
	// x = a1*p1 + k1 (mod lcm(a1,a2))
	ll lastk = 0,lasta = a[1];
	if (lasta + K - 1 > m) return false;
	for (int i = 2; i <= K; ++i) {
		ll k = (a[i] - i + 1) % a[i];
		Triple s(exgcd(lasta, a[i]));
		if ((k - lastk) % s.z) return false;
		const ll mod = a[i] / s.z;
		const ll times = (k - lastk) / s.z % mod;
		s.x %= mod;
		if (fabs((double)s.x * (double)times) > 1e15) s.x = (qmult(s.x,times,mod) + mod)%mod;
		else s.x = (s.x % mod * times % mod + mod) % mod;
		lastk += lasta * s.x;
		lasta = lasta / s.z * a[i];
		if ((lastk ? lastk : lasta) + K - 1 > m) return false;
	}
	lastk = lastk ? lastk : lasta;
	for (int i = 1; i <= K; ++i)
		if (gcd(lasta,lastk + i -1) != a[i]) return false;
	return true; 
}
void solve()
{
	if (!calc_row() || !calc_col()) puts("NO");
	else puts("YES");
}
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("D.in","r",stdin);
	freopen("D.out","w",stdout);
	#endif
	scanf("%I64d%I64d%I64d",&n,&m,&K);
	for (int i = 1; i <= K; ++i) scanf("%I64d",a + i);
	solve();
}

  

  E - Optimize!

  这题就比较简单了,方法有挺多的吧……我一开始想了个傻叉办法(SPlay……),麻烦得半死……

  先sort b[] (从小到大)

  令x[i] = min{j | s[i] + b[j] >= h} , y[i] = (x[idx] = i 的个数) , sum[i] = y[i] + y[i + 1] + ... + y[len]

  用各种数据结构维护之,然后如果min(len - i + 1 - sum[i]) >= 0 就给ans 加 1。 

  P.S. 注意细节处理。

#include <cstdio>
#include <algorithm>
const int N = 150000 + 9;
int a[N],min[N*4],tag[N*4],b[N],n,len,h,L,R,D;
inline void push_up(const int idx)
{min[idx] = std::min(min[idx * 2], min[idx * 2 + 1]);}
inline void push_down(const int idx)
{
	if (!tag[idx]) return;
	tag[idx * 2] += tag[idx]; tag[idx * 2 + 1] += tag[idx];
	min[idx * 2] += tag[idx]; min[idx * 2 + 1] += tag[idx];
	tag[idx] = 0;
}
void modify(const int idx,const int l,const int r)
{
	if (L <= l && r <= R) return (void)(min[idx] += D,tag[idx] += D);
	const int mid = (l + r) / 2;
	if (tag[idx]) push_down(idx);
	if (L <= mid) modify(idx * 2, l, mid);
	if (mid < R) modify(idx * 2 + 1, mid + 1, r);
	push_up(idx);
}
int main()
{
	#ifndef ONLINE_JUDGE
	freopen("E.in","r",stdin);
	freopen("E.out","w",stdout);
	#endif
	scanf("%d%d%d",&n,&len,&h);
	for (int i = 1; i <= len; ++i) {
		scanf("%d",b + i);
		L = R = i;
		D = len - i + 1;
		modify(1,1,len);
	}
	for (int i = 1; i <= n; ++i) {scanf("%d",a + i); a[i] = h - a[i];}
	std::sort(b + 1, b + 1 + len);
	int cnt = 0,ans = 0;
	for (int i = 1; i <= len; ++i) {
		if (a[i] > b[len]) {++cnt;continue;}
		if (i != 1 && a[i] <= 0) continue;
		L = 1; R = std::lower_bound(b + 1, b + 1 + len,a[i]) - b; D = -1;
		modify(1,1,len);
	}
	if (!cnt && min[1] >= 0) ++ans;
	for (int i = len + 1; i <= n; ++i) {
		if (a[i - len] <= b[len]) {
			L = 1; R = std::lower_bound(b + 1, b + 1 + len,a[i - len]) - b; D = 1;
			modify(1,1,len);
		}else --cnt;
		
		if (a[i - len + 1] <= 0) {
			L = 1; R = std::lower_bound(b + 1, b + 1 + len,a[i - len + 1]) - b; D = -1;
			modify(1,1,len);
		}

		if (a[i] > b[len]) {++cnt;continue;}
		if (a[i] > 0) {
			L = 1; R = std::lower_bound(b + 1, b + 1 + len,a[i]) - b; D = -1;
			modify(1,1,len);
		}
		
		if (!cnt && min[1] >= 0) ++ans;
	}
	printf("%d\n",ans);
}

  

转载于:https://www.cnblogs.com/lazycal/p/3268393.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值