NOIP模拟26「神炎皇·降雷皇·幻魔皇」
T1:神炎皇
又是数学题,气死,根本不会。
首先考虑式子\(a+b=ab\),我们取\(a\)与\(b\)的\(gcd\):\(d\),那么式子就可以改写成:
现在,有\(a'\)与\(b'\)互质,那么\(a'+b'\)一定不是\(a'b'\)的因子,这很显然,就不证了,那么\(a'+b'\)一定整除\(d\)。
\(\because\)\(a+b\)小于等于\(n\),且\(a'+b'\)小于或等于\(d\)(整除);
\(\therefore\)\(a'+b'\)小于或等于\(\sqrt{n}\);
枚举\(a'+b'=k\),可以知道\(d\)有\(n/k^{2}\)个,证明:
\(\because\) \((a'+b')|d\)
\(\therefore\) \((a'+b')*d=(a'+b')^{2}*x<=n\)
显然,\(d\)有\(x\)个。
另外,考虑合法的\(a'+b'\)的对数,可以知道是\(\phi\)\((k)\)个,证明见土哥的博客。
附上AC代码:
#include<bits/stdc++.h>
using namespace std;
namespace STD
{
#define rr register
#define inf LONG_LONG_MAX
typedef long long ll;
const ll N=100000000000000;
const int SIZE=1e7+4;
ll n,cnt;
ll ans;
ll prime[SIZE];
ll phi[SIZE];
bool isnotprime[SIZE];
ll read()
{
rr ll x_read=0,y_read=1;
rr char c_read=getchar();
while(c_read<'0'||c_read>'9')
{
if(c_read=='-') y_read=-1;
c_read=getchar();
}
while(c_read<='9'&&c_read>='0')
{
x_read=(x_read<<3)+(x_read<<1)+(c_read^48);
c_read=getchar();
}
return x_read*y_read;
}
void pre()
{
for(rr ll i=2;i<=SIZE;i++)
{
if(!isnotprime[i]) prime[++cnt]=i,phi[i]=i-1;
for(rr ll j=1;j<=cnt&&i*prime[j]<=SIZE;j++)
{
isnotprime[i*prime[j]]=1;
if(i%prime[j]==0)
{
phi[i*prime[j]]=phi[i]*prime[j];
break;
}else phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
}
};
using namespace STD;
int main()
{
pre();
n=read();
ll ceil=sqrt(n);
for(rr ll i=1;i<=ceil;i++)
{
ll sum=phi[i];
ll d=n/(i*i);
ans+=sum*d;
}
cout<<ans;
}
T2:降雷皇
太明显的一个数据结构优化DP了,求最长上升子序列即可,考场AC。
直接上代码:
#include<bits/stdc++.h>
using namespace std;
namespace STD
{
#define rr register
#define inf INT_MAX
typedef long long ll;
const int N=100004;
const int mod=123456789;
const int R=100003;
int n,type;
int r[N];
#define lc id<<1
#define rc id<<1|1
struct node
{
int maxn;
ll cnt;
node(){maxn=cnt=0;}
node(int maxn_,ll cnt_){maxn=maxn_,cnt=cnt_;}
};
class Line_tree
{
private:
node a[R<<2];
void Insert(int,int,int,int,node);
node Query(int,int,int,int,int);
public:
Line_tree(){}
void insert(int pos,node val){Insert(1,1,R-1,pos,val);}
node query(int st,int en)
{
if(st>en) return node(0,0);
return Query(1,1,R-1,st,en);
}
}t;
void Line_tree::Insert(int id,int l,int r,int pos,node val)
{
if(l==r)
{
if(a[id].maxn==val.maxn)
a[id].cnt=(a[id].cnt+val.cnt)%mod;
else if(a[id].maxn<val.maxn)
a[id]=val;
return;
}
int mid=(l+r)>>1;
if(pos<=mid) Insert(lc,l,mid,pos,val);
else Insert(rc,mid+1,r,pos,val);
a[id]=(a[lc].maxn>a[rc].maxn)?a[lc]:a[rc];
if(a[lc].maxn==a[rc].maxn)
a[id].cnt=(a[lc].cnt+a[rc].cnt)%mod;
}
node Line_tree::Query(int id,int l,int r,int st,int en)
{
if(st<=l&&r<=en) return a[id];
int mid=(l+r)>>1;
node ret=node(-inf,0),temp;
if(st<=mid)
{
temp=Query(lc,l,mid,st,en);
if(temp.maxn>ret.maxn)
ret=temp;
else if(temp.maxn==ret.maxn)
ret.cnt=(ret.cnt+temp.cnt)%mod;
}
if(mid<en)
{
temp=Query(rc,mid+1,r,st,en);
if(temp.maxn>ret.maxn)
ret=temp;
else if(temp.maxn==ret.maxn)
ret.cnt=(ret.cnt+temp.cnt)%mod;
}
return ret;
}
#undef lc
#undef rc
int read()
{
rr int x_read=0,y_read=1;
rr char c_read=getchar();
while(c_read<'0'||c_read>'9')
{
if(c_read=='-') y_read=-1;
c_read=getchar();
}
while(c_read<='9'&&c_read>='0')
{
x_read=(x_read<<3)+(x_read<<1)+(c_read^48);
c_read=getchar();
}
return x_read*y_read;
}
};
using namespace STD;
int main()
{
n=read(),type=read();
for(rr int i=1;i<=n;i++)
r[i]=read();
int ans=-inf;
ll num=0;
for(rr int i=1;i<=n;i++)
{
node temp=t.query(1,r[i]-1);
temp.maxn++;
if(temp.cnt==0)
temp.cnt=1;
if(ans<temp.maxn)
{
num=temp.cnt;
ans=temp.maxn;
}
else if(ans==temp.maxn)
num=(num+temp.cnt)%mod;
t.insert(r[i],temp);
}
cout<<ans;
if(type==1)
cout<<num;
}
T3:幻魔皇
首先很明显,任意两个白点的\(LCA\)要么是白的,要么是黑的,分类讨论即可。
首先观察一个性质,就是从第三层点开始,所有白点数目满足斐波那契数列,从第二层点开始,黑点满足斐波那契数列。
且,由于树的拓扑性质,所有子树也满足这种性质。
首先考虑\(LCA\)为白点的性质,那么就是\(LCA\)与儿子们匹配,直接用斐波那契数求即可。
至于说黑点的情况,就是儿子们两两匹配即可。
复杂度是\(O(n^{2}+n^{3})\)
前缀和优化可以到\(O(n+n^{2})\)
代码:
#include<bits/stdc++.h>
using namespace std;
namespace STD
{
#define rr register
#define inf INT_MAX
typedef long long ll;
const int N=5004;
const int mod=123456789;
int n;
ll fib[N];
ll sum[N];
ll ans[N<<1];
int read()
{
rr int x_read=0,y_read=1;
rr char c_read=getchar();
while(c_read<'0'||c_read>'9')
{
if(c_read=='-') y_read=-1;
c_read=getchar();
}
while(c_read<='9'&&c_read>='0')
{
x_read=(x_read<<3)+(x_read<<1)+(c_read^48);
c_read=getchar();
}
return x_read*y_read;
}
void pre()
{
fib[1]=1;
sum[1]=1;
for(rr int i=2;i<=5002;i++)
{
fib[i]=(fib[i-1]+fib[i-2])%mod;
sum[i]=(sum[i-1]+fib[i])%mod;
}
}
};
using namespace STD;
int main()
{
pre();
n=read();
for(rr int i=2;i<=n-1;i++)
ans[i]=fib[n-i]*fib[i-1]%mod;
for(rr int i=2;i<=n-2;i++)
for(rr int j=3;j<=n-2;j++)
{
int x=max(i,j);
ans[i+j]=(ans[i+j]+sum[n-x-1]*fib[i-1]%mod*fib[j-2]%mod)%mod;
}
for(rr int i=2;i<=n-2;i++)
ans[i+1]=(ans[i+1]+sum[n-i-1]*fib[i-1]%mod)%mod;
for(rr int i=1;i<=(n<<1);i++)
printf("%lld ",ans[i]);
}
学到了什么:
关于T1:
首先,我忘了如何线性求phi函数了,正好复习了一下。
考试的时候推了那个等式了,也除了\(GCD\)但是对phi函数这个东西不熟,结果啥也没搞出来,丢了个暴力就走了,还是对数论不熟,要复习了。。。。。。
关于T2:
考场AC还是很开心的,之前做过这种题,证明之前的知识都学到手了,还不错。
关于T3:
考场上看出性质了,原本搞了没有优化的正解(自我感觉是正解,实际上推了推,大体是对的),但是没时间码对,直接挂了,要是码力再强点,兴许有80分吧,谁知道呢,做这道时想起了之前做的一道有关斐波那契的题,以为巧妙编号就可以靠编号搞出\(LCA\)因此想歪了。。。。。。