noip模拟42 卷 简单题 粉丝 字符串
考出自集训以来的最好成绩。(可能也就这一次)
考场上顺序开题。
\(\mathrm{A.}\mathbb{卷}\):树形 \(\mathrm{dp}\)
\(\mathrm{B.}\mathbb{简单题}\):数学
\(\mathrm{C.}\mathbb{粉丝}\):数学?
\(\mathrm{D.}\mathbb{字符串}\):\(\mathrm{manacher}\)
都看了一遍,果断选择 \(\mathrm{B}\) 开,事实证明我的这个做法有一定风险,光推式子找规律加上写代码就花了整整三个小时,最后又花了二十分钟调了调 \(\mathrm{B}\) 的一些错误,这才开始看别的题。
看了一眼 \(\mathrm{A}\),这tm不就是树形 \(\mathrm{dp}\) 吗!至于乘积的部分可以运用与这道题(题解)类似的方法,用对数化乘为加,于是就以极快的手速在半个小时内搞定。
最后的半个小时基本没事干,看了一眼 \(\mathrm{C}\) 和 \(\mathrm{D}\),没啥可打的部分分,于是又回头去找 \(\mathrm{A}\) 和 \(\mathrm{B}\) 有没有错,就这样一直拖到了考试结束。
(其实 \(\mathrm{C}\) 还写了个 \(\mathrm{rand}\),只不过一分没有)
估分:\(\color{white}(100+100+0+0=\)\(2\)\(00\color{white})\)
实际:\(100+100+0+0=200\)
好耶(bushi
还是说正解吧(
A.卷
正解
记录乘积的 \(\log\) 值,由于数据随机生成,因此不会产生精度问题,而原来乘积的两个操作同样也都支持:
- 
比大小 
 由于 \(\color{white}\{x>y\ge\)\(1\),因此可以得出 \(\log x>\log y\)
- 
相乘 
 \(\log(xy)=\log x+\log y\)
剩下的就和普通的最大权独立集一模一样,设状态转移就好了。
时间复杂度:\(O(n)\)
code
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5,mod=1e9+7;
const double eps=1e-6;
int n;
long long w[N];
int cnt,head[N];
double dp[N][2];
long long mul[N][2];
struct Node
{
	int to,nxt;
}e[N*2];
void add(int u,int v)
{
	e[++cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt;
}
void dfs(int u,int fa)
{
	bool flag=0;
	dp[u][1]=log(w[u]);
	mul[u][1]=w[u]%mod;
	mul[u][0]=1;
	for(int i=head[u];i;i=e[i].nxt)
	{
		flag=1;
		int v=e[i].to;
		if(v==fa) continue;
		dfs(v,u);
		if(fabs(dp[v][0]+1)>eps) dp[u][1]+=dp[v][0];
		mul[u][1]=mul[u][1]*mul[v][0]%mod;
		dp[u][0]+=max(dp[v][0],dp[v][1]);
		if(dp[v][0]>dp[v][1]) mul[u][0]=mul[u][0]*mul[v][0]%mod;
		else mul[u][0]=mul[u][0]*mul[v][1]%mod;
	}
	if(!flag) dp[u][0]=-1;
}
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=x*10+ch-48;
		ch=getchar();
	}
	return x*f;
}
int main()
{
	n=read();
	for(int i=1;i<=n;i++) w[i]=read();
	for(int i=1;i<=n-1;i++)
	{
		int u=read(),v=read();
		add(u,v);
		add(v,u);
	}
	dfs(1,0);
	if(dp[1][0]>dp[1][1]) printf("%lld\n",mul[1][0]);
	else printf("%lld\n",mul[1][1]);
	return 0;
}
B.简单题
正解
把 \(p,2p,\)\(4\)\(p,\dots,2^kp\color{white}\}\)(\(p\) 为奇数)看作一条链,不难发现链的长度最多为 \(\log n\)。因此我们可以枚举链的长度,来计算链的个数,从而确定 \(|A|\) 的范围 \([l,r]\),不难发现,比 \(l\) 大的部分是一个组合数,\(\mathrm{Lucas}\) 即可。
歪解
设 \(f_{n,m}\) 为最终要求的答案,不难发现:
若 \(2\nmid n\),有 \(f_{n,m}=f_{n-1,m-1}+f_{n-1,m}\)。
因此奇数都可以转变为偶数解决。
再经过亿些打表,就很容易发现如下的式子:
其中:
\(ans\) 为最终答案;
\(cnt_0\) 表示在 \(1\sim n\) 中恰有大于等于 \(2\) 且为偶数个因子 \(2\) 的数的个数;
\(cnt_1\) 表示在 \(1\sim n\) 中恰有奇数个因子 \(2\) 的数的个数。
\(\mathrm{Lucas}\) 即可。
时间复杂度:\(O(mod+3q)\)
code
#include<bits/stdc++.h>
using namespace std;
long long n,m,t,cnt0,cnt1;
const int mod=10000019;
int q,cnt;
long long fac[mod],invfac[mod];
inline long long read()
{
	long long x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=x*10+ch-48;
		ch=getchar();
	}
	return x*f;
}
long long power(long long b,long long p)
{
	long long mul=1;
	while(p)
	{
		if(p&1) mul=mul*b%mod;
		p>>=1;
		b=b*b%mod;
	}
	return mul;
}
long long C(int x,int y)
{
	if(x<y) return 0;
	return fac[x]*invfac[y]%mod*invfac[x-y]%mod;
}
long long Lucas(long long x,long long y)
{
	if(y<0||x<y) return 0;
	if(y==0) return 1;
	return Lucas(x/mod,y/mod)*C(x%mod,y%mod)%mod;
}
int main()
{
	n=read();
	q=read();
	if(n&1) t=n-1;
	else t=n;
	while(t)
	{
		t>>=1;
		cnt++;
		if(cnt&1) cnt1+=((t+1)>>1);
		else cnt0+=((t+1)>>1);
	}
	long long pw=power(2,cnt1-cnt0);
	long long x1=2*cnt0;
	fac[0]=1;
	invfac[mod-1]=power(mod-1,mod-2);
	for(int i=mod-2;i>=0;i--) invfac[i]=invfac[i+1]*(i+1)%mod;
	for(int i=1;i<=mod-1;i++) fac[i]=fac[i-1]*i%mod;
	while(q--)
	{
		m=read();
		if(n&1) printf("%lld\n",pw*((Lucas(x1,m-cnt1)+Lucas(x1,m-cnt1-1))%mod)%mod);
		else printf("%lld\n",pw*Lucas(x1,m-cnt1)%mod);
	}
	return 0;
}
C.粉丝
考场上只写了一个 \(\mathrm{rand}\)
正解
考虑 \(\mathrm{dp}\)
- 
状态:设 \(f_{i,j}\) 表示当前枚举到数字 \(i\),且和为 \(j\) 的方案数。 
- 
设 \(g_{i,j}\) 表示当前枚举到第 \(i\) 个数,且和为 \(j\) 的方案数。 
不难发现,这两个状态分别计算都是 \(O(n^2)\) 的,但是如果将小于 \(\sqrt{n}\) 的数字全部用第一个 \(\mathrm{dp}\),将大于(等于)\(\sqrt{n}\) 的数字交给第二个 \(\mathrm{dp}\),两个 \(\mathrm{dp}\) 的复杂度均为 \(O(n\sqrt{n})\),因此可以过掉本题。
code
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int x,y,n,mod;
long long f[N],g[N],sum[N],ans;
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=x*10+ch-48;
		ch=getchar();
	}
	return x*f;
}
long long work(int lim)
{
	memset(f,0,sizeof(f));
	memset(g,0,sizeof(g));
	memset(sum,0,sizeof(sum));
	f[0]=g[0]=sum[0]=1;
	ans=0;
	int b=max((int)sqrt(n),lim);
	for(int i=lim;i<=b-1;i++)
	{
		for(int j=i;j<=n;j++) f[j]=(f[j]+f[j-i])%mod;
	}
	for(int i=1;i<=n/b;i++)
	{
		int t=i*b;
		for(int j=i;j<=n-t;j++) g[j]=(g[j]+g[j-i])%mod;
		for(int j=0;j<=n-t;j++) sum[j+t]=(sum[j+t]+g[j])%mod;
	}
	for(int i=0;i<=n;i++) ans=(ans+f[i]*sum[n-i]%mod)%mod;
	return ans;
}
int main()
{
	x=read();
	y=read();
	n=read();
	mod=read();
	printf("%lld\n",(work(x)-work(y+1)+mod)%mod);
	return 0;
}
D.字符串
正解
code

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号