2019.03.08晚【NOIP提高组】模拟 B 组

本文介绍了三道算法竞赛题目的解题思路及代码实现,包括树上倍增LCA模板解决祖孙关系查询问题、双指针法求解队伍得分期望值问题以及使用01背包和容斥原理计算好数字的个数。

前言

感觉不难呀


JZOJ 3054 祖孙询问

题目

求两个树上节点的祖孙关系


分析

树上倍增LCA模板,不想多说


代码

#include <cstdio>
#include <cctype>
#include <cstring>
#define rr register
using namespace std;
const int N=40011;
struct node{int y,next;}e[N<<1];
int n,ls[N],bj[N],f[N][17],dep[N],k=1,root,tot;
inline signed iut(){
	rr int ans=0,f=1; rr char c=getchar();
	while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans*f;
}
inline void add(int x,int y){
	e[++k]=(node){y,ls[x]}; ls[x]=k;
	e[++k]=(node){x,ls[y]}; ls[y]=k;
}
inline void dfs(int x,int fa){
	for (rr int i=ls[x];i;i=e[i].next)
	if (e[i].y!=fa){
		dep[e[i].y]=dep[x]+1;
		f[e[i].y][0]=x;
		dfs(e[i].y,x);
	}
}
inline signed lca(int x,int y){
	if (x==y) return x;
	if (dep[x]<dep[y]) x^=y,y^=x,x^=y;
	for (rr int i=16;~i;--i) if (dep[x]-(1<<i)>=dep[y]) x=f[x][i];
	if (x==y) return x;
	for (rr int i=16;~i;--i)
	if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
	if (x==y) return x;
	else return f[x][0];
}
signed main(){
	n=iut(); memset(f,-1,sizeof(f));
	for (rr int i=1;i<=n;++i){
		rr int x=iut(),y=iut();
		if (!bj[x]) bj[x]=++tot;
		if (y!=-1&&!bj[y]) bj[y]=++tot;
		if (y==-1) root=bj[x];
		    else add(bj[x],bj[y]);
	}
	dep[root]=1; dfs(root,0); n=tot;
	for (rr int j=1;(1<<j)<=n;++j)
	for (rr int i=1;i<=n;++i) if (f[i][j-1])
	    f[i][j]=f[f[i][j-1]][j-1];
	for (rr int m=iut();m;--m){
		rr int x=bj[iut()],y=bj[iut()];
		rr int t=lca(x,y);
		if (t==x) putchar(49);
			else if (t==y) putchar(50);
			    else putchar(48);
		putchar(10);
	}
	return 0;
} 

JZOJ 3055 比赛

题目

每个选手都有一个非负的实力值。如果实力值为 X X X Y Y Y的选手对抗,那么实力值较强的选手所在的队伍将会获得 ( X − Y ) 2 (X-Y)^2 (XY)2的得分。
A A A队伍的得分减 B B B队伍的得分的期望值。


分析

O ( n 2 ) O(n^2) O(n2)代码是行不通的,所以尝试用双指针 O ( n ) O(n) O(n),枚举到 i i i点,记录隔壁队伍不超过该实力的前缀和以及前缀平方和,然后把完全平方公式拆开分别求解


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
using namespace std;
typedef long long ll;
int n,a[50001],b[50001];
ll sum,sqrr,sumb,sqrb,ans;
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
signed main(){
	n=iut();
	for (rr int i=1;i<=n;++i) a[i]=iut();
	for (rr int i=1;i<=n;++i) b[i]=iut();
	sort(a+1,a+1+n); sort(b+1,b+1+n);
	for (rr int i=1;i<=n;++i) sum+=b[i],sqrr+=1ll*b[i]*b[i];
	for (rr int i=1,j=0;i<=n;++i){
		while (a[i]>b[j]&&j<=n) sumb+=b[j],sqrb+=1ll*b[j]*b[j],++j;
		ans+=1ll*a[i]*a[i]*((j<<1)-n-2)+((sum-(sumb<<1))*a[i]<<1)-sqrr+(sqrb<<1);
	}
	return !printf("%.1lf",ans*1.0/n);
}

JZOJ 3056 数字

题目

一个数字被称为好数字当他满足下列条件:

  1. 它有 2 n 2n 2n个数位, n n n是正整数(允许有前导0)
  2. 构成它的每个数字都在给定的数字集合 S S S中。
  3. 它前 n n n位之和与后 n n n位之和相等或者它奇数位之和与偶数位之和相等

已知 n n n,求合法的好数字的个数 m o d mod mod 999983 999983 999983


分析

可以用01背包的方法求出方案,因为前 n n n位之和=后 n n n位之和,奇数位之和=偶数位之和,所以前 n n n位中奇数位之和=后 n n n位中偶数位之和 且
n n n位中偶数位之和=后 n n n位中奇数位之和,所以处理出 n , ⌊ n 2 ⌋ n,\lfloor\frac{n}{2}\rfloor n,2n ⌈ n 2 ⌉ \lceil\frac{n}{2}\rceil 2n时01背包的方案,然后用容斥求解


代码

#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define rr register
using namespace std;
const int mod=999983;
typedef long long ll; int n,len,b[11];
ll ans,ans1,ans2,f[2][9101];
inline void init(){
	scanf("%d",&n); rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) b[++len]=c^48,c=getchar();
	sort(b+1,b+1+len);
}
inline ll modd(ll ans){
    rr int f=1;
    if (ans<0) f=-f,ans=-ans;
    if (ans>=(mod<<4)) ans=(ans-(mod<<4))%mod;
    else {
        ans-=(ans>=(mod<<3)?(mod<<3):0),
        ans-=(ans>=(mod<<2)?(mod<<2):0),
        ans-=(ans>=(mod<<1)?(mod<<1):0),
        ans-=(ans>=(mod<<0)?(mod<<0):0);
    }
    return ans*f;
}
signed main(){
    init(); f[0][0]=1; rr int t1=n>>1,t2=(n+1)>>1;
    if (!t1) ans1=1;
    for (rr int i=1;i<=n;++i){
    	memset(f[i&1],0,sizeof(f[i&1]));
    	for (rr int j=0;j<=b[len]*i;++j)
    	    for (rr int p=1;p<=len;++p)
		if (b[p]<=j) f[i&1][j]=modd(f[i&1][j]+f[(i&1)^1][j-b[p]]);
	    if (i==t1)
		    for (rr int j=0;j<=b[len]*i;++j) ans1=modd(ans1+f[i&1][j]*f[i&1][j]);
		if (i==t2)
		    for (rr int j=0;j<=b[len]*i;++j) ans2=modd(ans2+f[i&1][j]*f[i&1][j]);
	    if (i==n)
		    for (rr int j=0;j<=b[len]*i;++j) ans=modd(ans+(f[i&1][j]*f[i&1][j]<<1));
	}
	return !printf("%lld",modd(modd(ans-ans1*ans2)+mod));
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值