noip模拟13

这场考试整体状态还算不错,基本上没有挂分,不会的是真不会了……

pic.PNG

衡中巨佬有上200的


A. 工业题

其实这是组合题……

首先考虑从一个边上的点走到右下角,每往右走一次乘一个 a,每往下走一次乘一个 b,还需要乘上一个系数,这个系数就是走到右下角的方案数
这是一个经典的组合问题,答案为 (x+yx)

代码实现
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=998244353;
const int maxn=1e6+5;
int n,m,a,b,ans,fac[maxn],inv[maxn],x[maxn],y[maxn];
int read(){
	int x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		x=x*10+ch-48;
		ch=getchar();
	}
	return x*f;
}
int po(int a,int b=mod-2){
	int ans=1;
	while(b){
		if(b&1)ans=ans*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}
int c(int n,int m){
	if(n==m)return 1;
	return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
signed main(){
	n=read();
	m=read();
	a=read()%mod;
	b=read()%mod;
//	swap(a,b);
	for(int i=1;i<=n;i++){
		x[i]=read()%mod;
	}
	for(int i=1;i<=m;i++){
		y[i]=read()%mod;
	}
	fac[0]=1;
	inv[0]=1;
	for(int i=1;i<=n+m;i++){
		fac[i]=fac[i-1]*i%mod;
		inv[i]=po(fac[i]);
	}
	for(int i=1;i<=n;i++){
		ans=(ans+x[i]*po(a,m)%mod*po(b,n-i)%mod*c(m+n-i-1,m-1)%mod)%mod;		
	}
	for(int i=1;i<=m;i++){
		ans=(ans+y[i]*po(a,m-i)%mod*po(b,n)%mod*c(m+n-i-1,n-1)%mod)%mod;
	}
	cout<<ans<<endl;
	return 0;
}

B. 卡常题

其实这是图论题……

一眼望过去这种输入格式莫名有一种现成连边的亲切感,但是直接建出边后的图不知道是个啥(说实话其实是没看见保证图联通),想了半天网络流的东西
正解是 x 点相当于是带权点,y 点相当于需要覆盖的无权边,而 n 个点 n 条边的连通图是基环树
于是变成基环树上的边覆盖问题,随便拆掉换上的一条边进行 dp 即可
关于 dp,一种很方便的做法是以 x 为根来一遍,以 y 为根来一遍,之后取个最小值即可

代码实现
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
const int maxm=2e6+5;
int n,m,f[maxn][2],hd[maxn],cnt,ans,pos,val[maxn],a,b,x,y;
bool vis[maxn];
struct Edge{
	int nxt,to;
}edge[maxm];
void add(int u,int v){
	edge[++cnt].nxt=hd[u];
	edge[cnt].to=v;
	hd[u]=cnt;
	return ;	
}
int read(){
	int x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-')f=-1;
		ch=getchar();
	}	
	while(isdigit(ch)){
		x=x*10+ch-48;
		ch=getchar();	
	}
	return x*f;
}
void dfs(int u,int last){
	vis[u]=true;
	for(int i=hd[u];i;i=edge[i].nxt){
		int v=edge[i].to;
		if(i==(last^1))continue;
		if(vis[v]){
			pos=i;
			return ;
		}
		else dfs(v,i);
	}
	return ;
}
void dfs1(int u,int father){
	f[u][1]=val[u];
	for(int i=hd[u];i;i=edge[i].nxt){
		int v=edge[i].to;
		if(v==father||i==pos||i==(pos^1))continue;
		dfs1(v,u);
		f[u][0]+=f[v][1];
		f[u][1]+=min(f[v][0],f[v][1]);
	}
	return ;
}
int main(){
	n=read();
	a=read();
	b=read();
	cnt=1;
	for(int i=1;i<=n;i++){
		x=read();
		y=read();
		val[x]+=a;
		val[y]+=b;
		add(x,y);
		add(y,x);
	}
	dfs(1,0);
	x=edge[pos].to;
	y=edge[pos^1].to;
	dfs1(x,0);
	ans=f[x][1];
	memset(f,0,sizeof f);
	dfs1(y,0);
	cout<<min(ans,f[y][1]);
	return 0;
}

C. 玄学题

其实这是数学题……
首先一个来想 nm 的做法,有60分部分分的好成绩
一个直接的想法是能 O(1) 算出约数个数,这个可以线性筛(但是考场上忘了
再来重推一遍柿子
对于一个质数 p,约数个数为2;
如果 pi,显然 d[pi]=d[i]d[p]=d[i]2
否则,把柿子展开:

d[i]=(q1+1)(q2+1(qn+1)

d[ip]=(q1+1+1)(q2+1)(qn+1)

d[ip]=q1(q2+1)(qn+1)

可以看出

d[ip]=d[i]d[p]d[ip]

好的,现在来看正解:
因为这个奇怪的问法,发现 1 的次方只与奇偶有关
然后可以发现因数个数为偶数的数为完全平方数,那么问题变成 ij 有几个是完全平方数
i=qx2,那么j必须是 qy2 的形式
如果求出了q,个数即为 mq
那么q直接暴力筛即可

代码实现
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e7+5;
#define int long long
int n,m,p[maxn],ans; 
bool flag[maxn];
signed main(){
	cin>>n>>m;
	for(int i=1;i*i<=n;i++){
		for(int j=1;j*i*i<=n;j++){
			p[i*i*j]=j;
		}
		flag[i*i]=true;
	}
	int s=sqrt(m);
	for(int i=1;i<=n;i++){
		if(flag[i]){
			if(s&1)ans--;
			else ans++;	
		}
		else{
			int x=sqrt(m/p[i]);
			if(x&1)ans--;
			else ans++;	
		}
	}
	cout<<ans;
	return 0;
}
posted @   y_cx  阅读(63)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示