noip模拟13

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

pic.PNG

衡中巨佬有上200的


A. 工业题

其实这是组合题……

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

代码实现
#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;
如果 \(p\nmid i\),显然 \(d[p*i]=d[i]*d[p]=d[i]*2\)
否则,把柿子展开:

\[d[i]=(q_1+1)(q_2+1)……(q_n+1) \]

\[d[i*p]=(q_1+1+1)(q_2+1)……(q_n+1) \]

\[d[\frac{i}{p}]=q_1(q_2+1)……(q_n+1) \]

可以看出

\[d[i*p]=d[i]*d[p]-d[\frac{i}{p}] \]

好的,现在来看正解:
因为这个奇怪的问法,发现 \(-1\) 的次方只与奇偶有关
然后可以发现因数个数为偶数的数为完全平方数,那么问题变成 \(ij\) 有几个是完全平方数
\(i=q*x^2\),那么j必须是 \(q*y^2\) 的形式
如果求出了q,个数即为 \(\displaystyle\sqrt{\frac{m}{q}}\)
那么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 @ 2021-07-13 15:36  y_cx  阅读(61)  评论(0编辑  收藏  举报