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号