100+100+100+80,T4 \(O(n\log n)\) 没卡过,赛后没改 \(O(n)\),加了 WX 超级快读。
为啥放了套简单题,题目出处好像是 22csp 7连 day1。
A.上海
对 \(k\) 质因数分解,\(k=\sum\limits p_i^{c_i}\),使 \(n\) 最小且 \(k\mid n^2\) 就是使 \(n=\sum\limits p_i^{\lceil\frac{c_i}{2}\rceil}\)。
当 \(n=k\) 时没有解,其实这种情况下说明所有质因数的次数都是 \(1\);否则就有解的,时间复杂度 \(O(\sqrt k)\)。
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
long long k,tmp,ans=1;
int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
cin.tie(0),cout.tie(0);
ios::sync_with_stdio(0);
cin>>k;tmp=k;
for(int i=2;i<=sqrtl(tmp);++i)
{
int cnt=0;
for(;!(k%i);k/=i) ++cnt;
for(int j=1;j<=(cnt+1)/2;++j) ans*=i;
}
ans*=k;cout<<(ans==tmp?-1:ans)<<'\n';return 0;
}
B.华二
发现只有 \(6\) 拥有 \(2\) 个质因数,其它都只有一个。
所以先考虑只有 \(2\) 的倍数或 \(3\) 的倍数的数能组成多少种排列,发现任何数无法跨过 \(6\),同时其它的\(2\) 的倍数或 \(3\) 的倍数的数都相当于是已经排好顺序的。所以按 \(6\) 分割而开,每一段的排列数是 \(\dbinom{2\text{的倍数的个数}}{总个数}\),总排列数求积即可。
剩下就只有 \(1,5,7\),同理每个数都相当于独立排好顺序的,直接记录出现个数,然后就是合并几个排列了。时间复杂度 \(O(n)\)。
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=1e5+10,MOD=998244353;
int n,a[MAXN],cnt1,cnt2,cnt3,cnt4,cnt5;
long long ans=1,P[MAXN],inv[MAXN];
inline long long ksm(long long a,int b)
{
long long ans=1;
while(b)
{
if(b&1) ans=ans*a%MOD;
a=a*a%MOD,b>>=1;
}
return ans;
}
inline long long C(int n,int m)
{
if(n<m) return 0;
return P[n]*inv[m]%MOD*inv[n-m]%MOD;
}
int main()
{
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
cin.tie(0),cout.tie(0);
ios::sync_with_stdio(0);
cin>>n;
P[0]=1;for(register int i=1;i<=n;++i) P[i]=P[i-1]*i%MOD;
inv[n]=ksm(P[n],MOD-2);
for(register int i=n-1;i>=0;--i) inv[i]=inv[i+1]*(i+1)%MOD;
for(int i=1;i<=n;++i)
{
cin>>a[i];
if(a[i]==2||a[i]==4||a[i]==8) cnt1++;
if(a[i]==3||a[i]==9) cnt2++;
if(a[i]==1) cnt3++;
if(a[i]==5) cnt4++;
if(a[i]==7) cnt5++;
if(a[i]==6||i==n) ans=ans*C(cnt1+cnt2,cnt1)%MOD,cnt1=cnt2=0;
}
ans=ans*C(n-cnt4-cnt5,cnt3)%MOD*C(n-cnt5,cnt4)%MOD*C(n,cnt5)%MOD;cout<<ans<<'\n';return 0;
}
C.高爸
发现单峰,所以二分。发现不好维护,所以开个动态开点值域线段树,维护一下区间中下标和与个数和,用来求函数值。然后把二分改成线段树上二分,每次比较 \(F(mid)\) 与 \(F(mid+1)\) 的大小,决定递归到哪个儿子,时间复杂度 \(O(n\log n)\)。
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
const int INF=1e9;
int n,a,b,cnt=1;long long suma,sumb,cnta,cntb;
struct tree{long long sum;int cnt,ls,rs;}t[30000000];
inline int LS(int p){if(!t[p].ls) t[p].ls=++cnt;return t[p].ls;}
inline int RS(int p){if(!t[p].rs) t[p].rs=++cnt;return t[p].rs;}
inline void push_up(int p)
{
t[p].sum=t[t[p].ls].sum+t[t[p].rs].sum;
t[p].cnt=t[t[p].ls].cnt+t[t[p].rs].cnt;
return ;
}
inline void change(int l,int r,int p,int x)
{
if(l==r){t[p].sum+=x,t[p].cnt++;return ;}
int mid=(l+r)>>1;
if(x<=mid) change(l,mid,LS(p),x);
else change(mid+1,r,RS(p),x);
push_up(p);return ;
}
inline long long F(int x){return (cnta*x-suma)*a+(sumb-cntb*x)*b;}
inline void query(int l,int r,int p)
{
if(l==r)
{
suma+=t[p].sum,cnta+=t[p].cnt;
cout<<F(l)<<'\n';return ;
}
int mid=(l+r)>>1;
suma+=t[t[p].ls].sum,sumb+=t[t[p].rs].sum;
cnta+=t[t[p].ls].cnt,cntb+=t[t[p].rs].cnt;
if(F(mid)<F(mid+1))
{
suma-=t[t[p].ls].sum,cnta-=t[t[p].ls].cnt;
query(l,mid,LS(p));
}
else
{
sumb-=t[t[p].rs].sum,cntb-=t[t[p].rs].cnt;
query(mid+1,r,RS(p));
}
return ;
}
int main()
{
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
cin.tie(0),cout.tie(0);
ios::sync_with_stdio(0);
cin>>n>>a>>b;
for(int i=1,x;i<=n;++i)
{
cin>>x;change(0,INF,1,x);
suma=sumb=cnta=cntb=0;query(0,INF,1);
}
return 0;
}
D.金牌
将一条路径 \(l\to r\),拆成 \(l\to x,x\to y,y\to r\),设每段长度 \(a,b,c\),所以\(\sum 2^{a+b+c}=\sum (2^a\times 2^b\times 2^c)=2^b\times \sum 2^a\times\sum 2^c\)。
所以求出每个点本身及其子树中的点到它的权值和 \(d_x\) 与所有点到它的权值和 \(g_x\)。
对于一次询问 \(x,y\),记 \(L=\operatorname{lca}(x,y)\),钦定 \(dep_x\leq dep_y\)。
- 若 \(x\not = L\),则答案为 \(d_x\times d_y\times 2^{\operatorname{dist}(x,y)}\)。
- 若 \(x= L\),则记 \(y\) 的 \(dep_x-dep_y-1\) 级祖先为 \(k\),答案为 \((g_x-2d_k)\times d_y\times 2^{\operatorname{dist}(x,y)}\)。
先一遍 dfs
求出 \(d_x=1+\sum\limits_{y\in son(x)} 2d_y\)。
再一遍 dfs
求出 \(g_x=d_x+2(g_{fa_x}-2d_x)\)。
剩下就是求 \(\operatorname{lca}\) 与 \(k\) 级祖先,用树剖卡常,\(O(n\log n)\),建议用超级快读优化高达 \(10^6\) 的输入输出,写的清真一点可过。
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<time.h>
#include<string.h>
using namespace std;
const int MAXN=1e6+10,MOD=998244353;
int n,q,root,to[MAXN<<1],nxt[MAXN<<1],head[MAXN],cnt;
int dep[MAXN],fa[MAXN],siz[MAXN],hson[MAXN],top[MAXN];
long long d[MAXN],g[MAXN],P[MAXN];
namespace Octane {
//省略 WX 超级快读
} using namespace Octane;
inline void add(int x,int y)
{
to[++cnt]=y,nxt[cnt]=head[x];
head[x]=cnt;return ;
}
void dfs(int x)
{
d[x]=siz[x]=1,dep[x]=dep[fa[x]]+1;
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];if(y==fa[x]) continue;
fa[y]=x,dfs(y),d[x]+=(d[y]<<1),siz[x]+=siz[y];
if(siz[y]>siz[hson[x]]) hson[x]=y;
}
d[x]%=MOD;return ;
}
void dfs_2(int x)
{
g[x]=d[x]+(fa[x]?((g[fa[x]]-(d[x]<<1))<<1):0),g[x]%=MOD;
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];if(y==fa[x]) continue;
top[y]=(y==hson[x])?top[x]:y,dfs_2(y);
}
return ;
}
inline int lca(int x,int y)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
return x;
}
inline int get_fa(int x,int y)
{
while(dep[top[x]]>dep[y]+1) x=fa[top[x]];
return (top[x]==top[y])?hson[y]:top[x];
}
int main()
{
freopen("d.in","r",stdin);
freopen("d.out","w",stdout);
cin>>n;srand(time(0)),root=rand()%n+1;
for(int i=1,x,y;i<n;++i) cin>>x>>y,add(x,y),add(y,x);
dfs(root),top[root]=root,dfs_2(root);
P[0]=1;for(int i=1;i<=n;++i) P[i]=(P[i-1]<<1)%MOD;
cin>>q;
while(q--)
{
int x,y,L;cin>>x>>y;L=lca(x,y);
if(dep[x]>dep[y]) swap(x,y);
if(x!=L) cout<<d[x]*d[y]%MOD*P[dep[x]+dep[y]-(dep[L]<<1)]%MOD<<'\n';
else
{
int k=get_fa(y,x);
cout<<(d[y]*(g[x]-(d[k]<<1))%MOD*P[dep[x]+dep[y]-(dep[L]<<1)]%MOD+MOD)%MOD<<'\n';
}
}
return 0;
}
标签:cnt,NOIP,int,sum,long,include,模拟,MOD
From: https://www.cnblogs.com/int-R/p/NOIP9.html