2021.7.19提高B组模拟赛

2021.7.19 2021.7.19 2021.7.19 模拟赛 Ⅶ Ⅶ

由于放假一天 所以模拟赛编号 与游记编号会不一致


目录:

T1.玉米田(加强版)
T2.公约数的和
T3.只不过是长的领带
T4.概率充电器

T 1 : T1: T1玉米田 ( ( (加强版 ) ) )

在这里插入图片描述
L u o g u Luogu Luogu l i n k link link

分析:

洛谷的是弱化版
这个卡了空间 要用滚动数组 还要卡常
状压 d p   + dp~+ dp + 滚动数组 + + + 卡常是过不了的 但我是打表 + + + 卡常 + + + 状压 d p dp dp + + + 滚动数组嘿嘿
这个就不贴了

正解 : : 轮廓线 d p   + dp~+ dp + 滚动数组 + + + 卡常
假设 d p dp dp到了点 x x x 因为 x x x的后面和下面都还没有更新
x x x的取舍取决于 它的左边和上边
然后取出 u p up up l e f t left left的状态 处理不合法的状态 再转移

k k k为轮廓线的状态 方程:

f[i][k]+=f[i-1][k];  //不取(i,j)
f[i][k^(1<<j)]+=f[i-1][k];  //取(i,j)

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#pragma GCC optimize(2)
#include<cstring>
#define reg register
using namespace std;
const int N=20,Mod=100000000;
int n,m,a[N][N],f[2][(1<<N)],x;
int main(){
//	freopen("cowfood.in","r",stdin);
//	freopen("cowfood.out","w",stdout);
	scanf("%d%d",&n,&m);
	for(reg int i=0;i<n;i++)
		for(reg int j=0;j<m;j++)
			scanf("%d",&a[i][j]);
	f[0][0]=1;
	for(reg int i=0;i<n;i++)
	for(reg int j=0;j<m;j++)
	{
		x^=1;
		memset(f[x],0,sizeof(f[x]));
		for(reg int k=0;k<(1<<m);k++)
		{
			int up=(1<<j)&k,left=(j>0)?(1<<(j-1))&k:0;
			if((i==0&&up)||(j==0&&left)) continue;
			if(up)
			{
				f[x][k^(1<<j)]=(1ll*f[x][k^(1<<j)]+f[x^1][k])%Mod;
				continue;
			}
			if(left||a[i][j]==0)
			{
				f[x][k]=(1ll*f[x][k]+f[x^1][k])%Mod;
				continue;
			}
			f[x][k]=(1ll*f[x][k]+f[x^1][k])%Mod;
			f[x][k^(1<<j)]=(1ll*f[x][k^(1<<j)]+f[x^1][k])%Mod;
		}
	}
	int ans=0;
	for(reg int i=0;i<(1<<m);i++)
		ans=(1ll*ans+f[x][i])%Mod;
	printf("%d",ans);
	return 0;
}

T 2 : T2: T2公约数的和

在这里插入图片描述
L u o g u Luogu Luogu l i n k link link

分析:

洛谷的是弱化版

原式化为
∑ i = 1 n ∑ j = 1 n g c d ( i , j ) ∑ d = 1 n d ∑ i = 1 n ∑ j = 1 n   [ g c d ( i , j ) = d ] ∑ d = 1 n d ∑ i × d < = n ∑ j × d < = n   [ g c d ( i , j ) = 1 ] ∑ d = 1 n d ( 2 ∑ i = 1 ⌊ n d ⌋ φ ( i ) − 1 ) \sum_{i=1}^n\sum_{j=1}^ngcd(i,j)\\ \sum_{d=1}^nd\sum_{i=1}^n\sum_{j=1}^n~[gcd(i,j)=d]\\ \sum_{d=1}^nd\sum_{i\times d<=n}\sum_{j\times d<=n}~[gcd(i,j)=1]\\ \sum_{d=1}^nd(2\sum_{i=1}^{⌊\frac{n}{d}⌋ }φ(i)-1) i=1nj=1ngcd(i,j)d=1ndi=1nj=1n [gcd(i,j)=d]d=1ndi×d<=nj×d<=n [gcd(i,j)=1]d=1nd(2i=1dnφ(i)1)

后面 2 ∑ i = 1 ⌊ n d ⌋ φ ( i ) − 1 2\sum_{i=1}^{⌊\frac{n}{d}⌋ }φ(i)-1 2i=1dnφ(i)1可以线性求欧拉函数 然后前缀和

∑ i = 1 ⌊ n d ⌋ \sum_{i=1}^{⌊\frac{n}{d}⌋ } i=1dn可以整除分块做 复杂度 O ( T n ) O(T\sqrt n) O(Tn )

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define reg register
#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
const int N=1e6+5;
int T,prime[N],phi[N],tot,vis[N];
ll f[N],n,sum[N];
void Phi()
{
	phi[1]=1;
	for(reg int i=2;i<=N;i++)
	{
		if(!vis[i])
		{
			phi[i]=i-1;
			prime[++tot]=i;
		}
		for(reg int j=1;prime[j]*i<=N&&j<=tot;j++)
		{
			vis[prime[j]*i]=1;
			if(i%prime[j]==0)
			{
				phi[prime[j]*i]=phi[i]*prime[j];
				break;
			}
			phi[prime[j]*i]=phi[i]*(prime[j]-1);
		}
	}
	for(reg int i=1;i<=N;i++)
		sum[i]=sum[i-1]+phi[i];
}
ll query(ll x)
{
	ll res=0; 
	for(ll l=1,r=1;l<=x;l=r+1)
	{
		r=x/(x/l);
		res+=(2*sum[x/l]-1)*(r-l+1)*(r+l)/2;
	}
	ll qwq=x*(x+1)/2;
	return (res-qwq)/2;
}
int main(){
	scanf("%d",&T);
	Phi();
	while(T--)
	{
		scanf("%lld",&n);
		printf("%lld\n",query(n));
	}
	return 0;
}

T 3 : T3: T3只不过是长的领带

在这里插入图片描述

在这里插入图片描述
L u o g u   l i n k Luogu~link Luogu link

分析:

很简单的贪心思路 : : 小的配对小的 大的配对大的

n 2 n^2 n2枚举也不行 就用 s u m i sum_i sumi s u f i suf_i sufi 记录 i i i前面和后面的最大奇怪感
然后 s u m sum sum s u f suf suf m a x max max即可

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=2e5+5;
int n,b[N],tmp,ans[N],sum[N],suf[N];
struct qaq{
	int val,id;
}a[N];
bool cmp(qaq x,qaq y){return x.val<y.val;}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n+1;i++)
	{
		scanf("%d",&a[i].val);
		a[i].id=i;
	}
	for(int i=1;i<=n;i++)
		scanf("%d",&b[i]);
	sort(b+1,b+n+1);
	sort(a+1,a+n+2,cmp);
	for(int i=1;i<=n;i++)
		sum[i]=max(sum[i-1],max(a[i].val-b[i],0));
	for(int i=n+1;i>1;i--)
		suf[i]=max(suf[i+1],max(a[i].val-b[i-1],0));
	for(int i=1;i<=n+1;i++)
		ans[a[i].id]=max(sum[i-1],suf[i+1]); 
	for(int i=1;i<=n+1;i++)
		printf("%d ",ans[i]);
	return 0;
}

T 4 : T4: T4概率充电器

在这里插入图片描述
在这里插入图片描述
L u o g u   l i n k Luogu~link Luogu link

分析:

g m o j gmoj gmoj上的数据更强点 这个做法爆栈了 ( l u o g u (luogu (luogu可过 ) ) )

i i i自己亮的概率为 t i t_i ti ( x , y ) (x,y) (x,y)通电概率为 e d g e x , y edge_{x,y} edgex,y
p x p_x px为点 x x x不亮的概率 因为如果设亮的概率 式子会比较麻烦
p x = ( 1 − t x ) ∏ x , y ∈ E ( 1 − e d g e x , y + e d g e x , y × p y ) p_x=(1-t_x)\prod_{x,y∈E}(1-edge_{x,y}+edge_{x,y}\times p_y) px=(1tx)x,yE(1edgex,y+edgex,y×py)
即自己不能亮 边也不能通电 边导电相邻点就不能导电

然后要换根 d p dp dp
f x f_x fx x x x不被它子树的点 点亮的概率
f x = ( 1 − t x ) ∏ y ∈ s o n x ( 1 − e d g e x , y + e d g e x , y × p y ) f_x=(1-t_x)\prod_{y∈son_x}(1-edge_{x,y}+edge_{x,y}\times p_y) fx=(1tx)ysonx(1edgex,y+edgex,y×py)
d f s dfs dfs求出 f f f 这时根节点的答案是求出的 但其他点还没有

g x g_x gx表示以 x x x为根 x x x的答案
分为两部分:原来的子树 剩余的部分
x x x原来的子树已得出了 剩余的部分就是 g f a g_{fa} gfa减去 x x x f a fa fa的贡献

P = g f a 1 − e d g e f a , x + e d g e f a , x × f x P=\frac{g_{fa}}{1-edge_{fa,x}+edge_{fa,x}\times f_x} P=1edgefa,x+edgefa,x×fxgfa
g x = f x ( 1 − e d g e f a , x + e d g e f a , x × P ) g_x=f_x(1-edge_{fa,x}+edge_{fa,x}\times P) gx=fx(1edgefa,x+edgefa,x×P)

再跑 d f s dfs dfs求出 g g g 最后统计 1 − g i 1-g_i 1gi就是通电的概率了

b f s bfs bfs做法不会爆栈 后面会 u p d a t e update update

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=5e5+5;
int n,tot,head[N];
double p[N],ans,f[N],ovo[N];
struct node{
	int to,next;
	double k;
}a[N<<1];
void add(int x,int y,double k)
{
	a[++tot]=(node){y,head[x],k};
	head[x]=tot;
}
void dfs(int x,int fa)
{
	f[x]=1-p[x];
	for(int i=head[x];i;i=a[i].next)
	{
		int qwq=a[i].to;
		if(qwq==fa) continue;
		dfs(qwq,x);
		f[x]=f[x]*(1-a[i].k+a[i].k*f[qwq]);
	}
}
void query(int x,int fa,int els)
{
	if(x==1) ovo[x]=f[x];  //x是根 已经求出就不用做了
	else
	{
		double P=ovo[fa]/(1-a[els].k+a[els].k*f[x]);
		ovo[x]=f[x]*(1-a[els].k+a[els].k*P);
	}
	for(int i=head[x];i;i=a[i].next)
	{
		int qwq=a[i].to;
		if(qwq==fa) continue;
		query(qwq,x,i);
	}
}
int main(){
//	freopen("charger.in","r",stdin);
//	freopen("charger.out","w",stdout);
	scanf("%d",&n);
	for(int i=1,x,y,k;i<n;i++)
	{
		scanf("%d%d%d",&x,&y,&k);
		add(x,y,k*0.01);
		add(y,x,k*0.01);
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%lf",&p[i]);
		p[i]=p[i]*0.01;
	}
	dfs(1,0);
	query(1,0,0);
	for(int i=1;i<=n;i++)
		ans+=1-ovo[i];
	printf("%.6lf",ans);
	return 0;
} 

u p d : b f s upd:bfs updbfs做法

其实就是用 q u e u e queue queue代替 d f s dfs dfs的递归 但这样就不会爆栈了
注意最好记录下编号

( ( (手写栈 d f s dfs dfs不知道行不行 ) ) )

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue> 
using namespace std;
const int N=5e5+5;
int n,tot,head[N],dfn[N];
double p[N],ans,f[N],ovo[N];
struct node{int to,next; double k;}a[N<<1];
struct qaq{int x,fa;};
struct awa{int id,dep; double valF;}kel[N];
struct ouo{int x,fa; double valG;};
void add(int x,int y,double k)
{
	a[++tot]=(node){y,head[x],k};
	head[x]=tot;
}
bool cmp(awa x,awa y){return x.dep>y.dep;}
void bfs()
{
	queue<qaq> q;
	q.push((qaq){1,0});
	while(!q.empty())
	{
		qaq now=q.front();
		int x=now.x,fa=now.fa;
		q.pop();
		for(int i=head[x];i;i=a[i].next)
		{
			int qwq=a[i].to;
			if(qwq==fa) continue;
			kel[qwq].dep=kel[x].dep+1;
			q.push((qaq){qwq,x});
		}
	}
	for(int i=1;i<=n;i++)
		kel[i].id=i;
	sort(kel+1,kel+n+1,cmp);
	for(int i=1;i<=n;i++)
		dfn[kel[i].id]=i;
	for(int i=1;i<=n;i++)
	{
		kel[i].valF=1-p[kel[i].id];
		for(int j=head[kel[i].id];j;j=a[j].next)
		{
			int qwq=dfn[a[j].to];
			if(kel[i].dep+1==kel[qwq].dep)
				kel[i].valF=kel[i].valF*(1-a[j].k+a[j].k*kel[qwq].valF);
		}
	}
	for(int i=1;i<=n;i++)
		f[kel[i].id]=kel[i].valF;
}
void query()
{
	queue<ouo> q;
	ovo[1]=f[1];  //同理 如果是根就不用做
	q.push((ouo){1,0,0});
	while(!q.empty())
	{
		ouo now=q.front();
		int x=now.x,fa=now.fa;
		q.pop();
		for(int i=head[x];i;i=a[i].next)
		{
			int qwq=a[i].to;
			if(qwq==fa) continue;
			double P=ovo[x]/(1-a[i].k+a[i].k*f[qwq]);
			ovo[qwq]=f[qwq]*(1-a[i].k+a[i].k*P);
			q.push((ouo){qwq,x,a[i].k});
		}
	}
}
int main(){
//	freopen("charger.in","r",stdin);
//	freopen("charger.out","w",stdout);
	scanf("%d",&n);
	for(int i=1,x,y,k;i<n;i++)
	{
		scanf("%d%d%d",&x,&y,&k);
		add(x,y,k*0.01);
		add(y,x,k*0.01);
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%lf",&p[i]);
		p[i]=p[i]*0.01;
	}
	bfs();
	query();
	for(int i=1;i<=n;i++)
		ans+=1-ovo[i];
	printf("%.6lf",ans);
	
	return 0;
} 

赛后放了个 T 1 T1 T1的再加强版 插头 d p dp dp 我爬了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值