NOIP 模拟9

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;
}
posted @ 2023-11-02 15:11  int_R  阅读(204)  评论(3编辑  收藏  举报