NOIP提高组模拟赛20

A. 星际旅行

乍一看好像挺难,考场上跳过做后面,回来一想也不是多难

大胆猜测结论,然后居然证出来了,然而没有开longlong我直接爆炸

考场乱画发现猜测两边必须相邻,然后尝试证明

发现:一条边走两次=两条边,走一次=删一条边

然后问题就变成删去两条边,剩下的构成欧拉路

删去之前所有点的度数都是偶数,删去一条边会使两个点奇偶性改变,如果存在欧拉路,那么至多存在两个点度数为奇数,那么删去的两条边必然有一个公共点,才能满足性质

两个注意

一是自环,选择一个自环后所有点的度数仍为偶数,删去任何一条边都满足,所以自环需要特判

二是图可能不连通,这里的不联通是边不联通,与点无关,从有边的任意点开始DFS,经过点打个记号,最后判一下所有边连接的点是否都有记号即可

三是记得开longlong

code
#include <cstring>
#include <cstdio>
using namespace std;
const long long maxn=100005;
struct edge{
	long long to,net;
}e[maxn<<1|1];
long long d[maxn],tot=1,zh,head[maxn];
long long n,m;
bool f[maxn];

void add(long long u,long long v){
	e[++tot].net=head[u];
	head[u]=tot;
	e[tot].to=v;
}
void DFS(long long x){
	f[x]=1;
	for(long long i=head[x];i;i=e[i].net){
		long long v=e[i].to;
		if(f[v])continue;
		DFS(v);
	}
}
long long work(){
	DFS(e[tot].to);
	for(long long i=2;i<=tot;i+=2)
	  if(f[e[i].to]==0||f[e[i^1].to]==0)return 0;
	long long cnt=m,ans=0;
	for(long long i=1;i<=zh;++i){
		ans+=cnt-1;
		--cnt;
	}
	for(long long i=2;i<=tot;i+=2){
		if(e[i].to==e[i^1].to)continue;
		--d[e[i].to];--d[e[i^1].to];
		ans+=d[e[i].to]+d[e[i^1].to];
	}
	return ans;
}
int main(){
	scanf("%lld%lld",&n,&m);
	for(long long i=1;i<=m;++i){
		long long u,v;scanf("%lld%lld",&u,&v);
		add(u,v);add(v,u);
		if(u!=v)++d[u],++d[v];
		else ++zh;
	}
	printf("%lld\n",work());
	return 0;
}

B. 砍树

考场先打的这题,以为就是个二分水题,虽然复杂度过于离谱但是没有细想,交题跑路,不过还好拿到40pts

二分没啥说的,正解一会再讲,先看看有意思的

由于昨天有题需要随机打乱,改题时候我突发奇想加了个rand,结果发现A了之前没有A的点WA了没有WA的点

发现问题在于有的时候二分右界不应该缩的时候缩了,那么令右界有概率的缩呢?

else if(rand()%13==0)r=mid;

40>50

继续减小概率

else if(rand()%233==0)r=mid;

啪的一下,很快啊50>90

WC,随机化有搞头,于是我成功带偏机房,都开始尝试rand

我尝试继续rand然后

image

其实还是失败了,因为最后A的那个有面向数据

if(check(9297884))l=9297884;

不过这个启发我们不会正解就乱搞

然后让我们谈谈正解

题目其实是求

i1naiddi=1nai<=k

C=i=1nai+k

原式i1naid<=C/d

然后发现C/d取值有限,当他的值确定下来,左边的d显然越大越好,然后可以数论分块

code
#include<cstdio>
using namespace std;

typedef long long ll;
const int maxn=105;
ll a[maxn],n,k;
int main(){
	scanf("%lld%lld",&n,&k);
	for(int i=1;i<=n;++i)scanf("%lld",&a[i]);
	for(int i=1;i<=n;++i)k+=a[i];
	ll ans=1;
	for(ll l=1,r;l<=k;l=r+1){
		r=k/(k/l);
		ll sum=0;
		for(int i=1;i<=n;++i){
			sum+=a[i]/r;
			if(a[i]%r)++sum;
		}
		if(sum*r<=k)ans=r;
	}
	printf("%lld\n",ans);
	return 0;
}

rand+二分90pts代码

rand
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=105;
ll l=1,r=1e11+1e9+3;
ll a[maxn],n,k;
ll ls[maxn];
bool check(ll x){
	for(int i=1;i<=n;++i)ls[i]=a[i]%x;
	ll sum=0;
	for(int i=1;i<=n;++i)if(ls[i])sum+=x-ls[i];
	if(sum>k)return false;
	return true;
}
ll work(){
	while(l<r){
		if(r-l<=5){
			for(ll i=r;i>=l;--i)if(check(i))return i;
		}
		ll mid=rand()%(r-l+1)+l;
		if(check(mid))l=mid;
		else if(rand()%233==0)r=mid;
	}
	return l;
}
int main(){
	srand(time(NULL));
	scanf("%lld%lld",&n,&k);
	for(int i=1;i<=n;++i)scanf("%lld",&a[i]);
	printf("%lld\n",work());
	return 0;
}

C. 超级树

这几天打数论打傻了,看到读入mod就想排列组合exlucasexcrt。。。。想偏了也没来得及打

正解DP,一个定义就很诡异的DP

DP[i][j]表示一棵i_超级树中,有j条不经过相同点的路径 的方案数

转移时候用类似拼接的思想i_超级树=两个(i-1)_超级树+1个节点

既然是拼接,刷表法会省不少事

考虑dp[i][l]dp[i][r]能更新哪些位置

sum=dp[i][l]dp[i][r]

考虑新加入的点

  • 不选

>dp[i+1][l+r]+=sum

  • 选,单独成一条路径

>dp[i+1][l+r+1]+=sum

  • 选,且放入左/右子树的一条路径中,可以放在路径的两个端点(*2),选择任意一条路径(l+r)

>dp[i+1][l+r]+=(l+r)sum2

  • 选,连接左右子树,先左后右先右后左不同(2),路径选择任意(lr)

>dp[i+1][l+r1]+=sumlr2

  • 选,连接左子树两条路径,或者右子树两条路径

>dp[i+1][l+r1]+=sum(l(l1)+r(r1))

完了,代码非常简单,但是就是想不到。。。。

我太菜了

code
#include<cstdio>
using namespace std;

typedef long long ll;
ll n,mod;
ll dp[305][605];

int main(){
	scanf("%lld%lld",&n,&mod);
	dp[1][0]=dp[1][1]=1%mod;
	for(int i=1;i<n;++i){
		for(int l=0;l<=n;++l){
			for(int r=0;r<=n;++r){
				ll sum=dp[i][l]*dp[i][r]%mod;
				dp[i+1][l+r]+=sum;
				dp[i+1][l+r+1]+=sum;
				dp[i+1][l+r]+=2*sum*(l+r);
				dp[i+1][l+r-1]+=sum*l*r*2;
				dp[i+1][l+r-1]+=sum*(l*(l-1)+r*(r-1));
				dp[i+1][l+r-1]%=mod;
				dp[i+1][l+r+1]%=mod;
				dp[i+1][l+r]%=mod;
			}
		}
	}
	printf("%lld\n",dp[n][1]%mod);
	return 0;
}

D. 求和

本场水题,乍一看什么k次方,我就回忆起了被斯特林反演支配的恐惧(现在还是不会)

然后仔细一看,这玩意打个表不就行了?

果断切掉

code
#include <cstring>
#include <cstdio>
using namespace std;

const int mod=998244353;
const int maxn=300005;
typedef long long ll;

struct edge{
   int net,to;
}e[maxn<<1|1];
int head[maxn],tot;
void add(int u,int v){
   e[++tot].net=head[u];
   head[u]=tot;
   e[tot].to=v;
}

int rem[maxn][53],fa[maxn][23],dep[maxn],md;

void DFS(int x){
	md=md>dep[x]?md:dep[x];
	for(int i=head[x];i;i=e[i].net){
		int v=e[i].to;
		if(v==fa[x][0])continue;
		dep[v]=dep[x]+1;
		fa[v][0]=x;
		DFS(v);
	}
}

int LCA(int u,int v){
	if(dep[v]>dep[u]){
		u^=v;v^=u;u^=v;
	}
	for(int i=20;i>=0;--i)if(dep[u]-dep[v]>=(1<<i))u=fa[u][i];
	if(u==v)return u;
	for(int i=20;i>=0;--i)if(fa[u][i]!=fa[v][i])u=fa[u][i],v=fa[v][i];
	return fa[u][0];
}

int main(){
	int n;scanf("%d",&n);
	for(int i=1;i<n;++i){
		int u,v;scanf("%d%d",&u,&v);
		add(u,v);add(v,u);
	}
	dep[1]=0;DFS(1);
	for(int i=1;i<=md;++i)rem[i][1]=i;
	for(int i=1;i<=md;++i)
	  for(int j=2;j<=50;++j)
	    rem[i][j]=1ll*rem[i][j-1]*i%mod;
	for(int j=1;j<=50;++j)
	  for(int i=1;i<=md;++i)
	    rem[i][j]=(1ll*rem[i][j]+rem[i-1][j])%mod;
	for(int j=1;j<=20;++j)
	  for(int i=1;i<=n;++i)
	    fa[i][j]=fa[fa[i][j-1]][j-1];
	int m;scanf("%d",&m);
	for(int i=1;i<=m;++i){
		int u,v,k;scanf("%d%d%d",&u,&v,&k);
		int lca=LCA(u,v);
		ll ans=1ll*rem[dep[u]][k]+rem[dep[v]][k]-rem[dep[lca]][k];
		if(dep[lca]-1>0)ans-=rem[dep[lca]-1][k];
		ans=(ans%mod+mod)%mod;
		printf("%lld\n",ans);
	}
	return 0;
}

posted @   Chen_jr  阅读(56)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示