首页 > 其他分享 >NOIP2023模拟9联测30

NOIP2023模拟9联测30

时间:2023-11-03 17:13:44浏览次数:42  
标签:now ch int top 30 联测 lca NOIP2023 include

这篇博客是第二天赛时写的。(恼)

T1

数学题。

肯定是想把 \(k\) 质因数分解,然后找一找规律,发现对于一个最小的 \(n\) 一定不包括除了 \(k\) 有的质因子以外的其他质因子,因为其他质因子对是不是 \(k\) 的倍数没有用。

\(n^2\) 相当于把 \(n\) 的所有质因子的指数乘了个 \(2\), 那么只要保证这个指数乘 \(2\) 后大于等于 \(k\) 的相同的质因子的指数,那么就是满足条件,要保证最小,直接把 \(k\) 质因子指数除以二向上取整就好了。

发现数很大,没办法全部质因数分解。发现对于大于 \(\sqrt{k}\) 的质因子的指数一定不大于一,那直接算到 \(\sqrt{k}\), 剩下的直接乘就可以了。

记得判无解情况,即 \(n=k\) 时。

Code
#include <iostream>
#include <cstdio>

#define int long long

const int N=1e7+10;

using namespace std;

inline int read() {
    int f=1, x=0;
    char ch=getchar();
    while(ch>'9' || ch<'0') {
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0' && ch<='9') {
        x=(x<<3)+(x<<1)+(ch^48);
        ch=getchar();
    }
    return f*x;
}

inline void write(int x) {
    cout<<x; putchar('\n');
}

int k, tot, ans;
int prime[N], is_prime[N], cnt[N];

void pre_work() {
    for(int i=2;i<=N-5;i++) {
        if(!is_prime[i]) prime[++tot]=i;
        for(int j=1;j<=tot && prime[j]*i<=N-5;j++) {
            is_prime[prime[j]*i]=1;
            if(i%prime[j]==0) break;
        }
    }
}

inline int get_(int x,int k) {
    if(x%k==0) return x/k;
    else return (x/k)+1;
} 

inline int fpow(int x,int k) {
    int res=1;
    while(k) {
        if(k&1) res*=x;
        x=x*x;
        k>>=1;
    }
    return res;
}

signed main() {

    pre_work();

    k=read();
    
    int now=k;
    for(int i=1;i<=tot && prime[i]<=now;i++) {
        if(now%prime[i]==0) {
            while(now%prime[i]==0) {
                cnt[i]++;
                now/=prime[i];
            }
        }
    }

    int ans=now;
    for(int i=1;i<=tot;i++) {
        if(cnt[i]) {
            ans=ans*fpow(prime[i], get_(cnt[i], 2));
        }
    }
    if(ans==k) write(-1);
    else write(ans);

    return 0;
}

T2

Part1

发现值域只有 \([1,9]\),那么直接分讨,对于 \(1, 5, 7\) 这三个数,和其他数的 \(gcd\) 都为 \(1\),又因为相同数没有区别,所以他们仨可以随便放。先分出来单独讨论。

Part2

剩下的 \(2,3,4,6,8,9\),发现他们的公共质因子只有 \(2,3\), 而 \(6\) 又都包含,说明 \(6\) 不能换,只能在这里,那么若干个 \(6\) 把序列分成了若干段,段与段之间互不影响。

考虑段内的贡献,发现公共质因子为 \(2\) 的几个数相对位置不会变化,为 \(3\) 的也不会变化,这两种数没有交集,互不影响,那么可以直接组合数算,设公共质因子为 \(2\) 的数有 \(n\) 个,为 \(3\) 的数有 \(m\) 个,那贡献就是 \(\dbinom{n+m}{n}\),最后全部乘起来就好了。

对于 \(1,5,7\) 的贡献,和上面差不多,设 \(1\) 的总数为 \(n\),当前数的个数为 \(m\),贡献即为 \(\dbinom{n+m}{n}\),\(5\) 和 \(7\) 也是一样的。

复杂度为 \(\Theta(n)\)。

Code
#include <iostream>
#include <cstdio>

#define int long long  

const int mod=998244353;
const int N=1e5+10;

using namespace std;

inline int read() {
    int f=1, x=0;
    char ch=getchar();
    while(ch>'9' || ch<'0') {
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0' && ch<='9') {
        x=(x<<3)+(x<<1)+(ch^48);
        ch=getchar();
    }
    return f*x;
}

inline void write(int x) {
    cout<<x; putchar('\n');
}

int n, tot, ans;
int a[N], cnt[N];
int fac[N], inv[N], invj[N];

void prework() {
    fac[0]=fac[1]=inv[0]=inv[1]=invj[0]=invj[1]=1;
    for(int i=2;i<=n+5;i++) {
        fac[i]=fac[i-1]*i%mod;
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;
        invj[i]=invj[i-1]*inv[i]%mod;
    }
    ans=1;
}

inline int C(int n,int m) {
    if(m>n) return 0;
    return fac[n]*invj[n-m]%mod*invj[m]%mod;
}

inline void solve(int l,int r) {
    if(l>=r) return;
    int sum1=0, sum2=0;
    for(int i=l;i<=r;i++) {
        if(a[i]%2==0) sum1++;
        else sum2++;
    }
    int sum=C(sum1+sum2, sum1);
    ans=(ans*sum)%mod;
}

signed main() {
    
    n=read();

    prework();

    for(int i=1;i<=n;i++) {
        int x=read();
        if(x==1 || x==5 || x==7) {
            cnt[x]++;
        }
        else a[++tot]=x;
    }
    
    int pre=1;
    for(int i=1;i<=tot;i++) {
        if(a[i]==6) {
            solve(pre, i-1);
            pre=i+1;
        }
    }
    solve(pre, tot);

    tot+=cnt[1], ans=(ans*C(tot, cnt[1]))%mod;
    tot+=cnt[5], ans=(ans*C(tot, cnt[5]))%mod;
    tot+=cnt[7], ans=(ans*C(tot, cnt[7]))%mod;

    write(ans);

    return 0;
}

T3

Part1

设最终的力量值为 \(x\),首先发现将 \(x\) 设置为原有的一条龙的力量值一定更优,证明考虑移动 \(x\),在龙与龙之间时的图像是一条直线,那么最值一定出在一个拐点上,也就是原有的一条龙的力量值。

考虑刻画出函数图像,对于单个一条龙,函数图像一定是一个凸函数,又因为凸函数也是凸函数,那么多条龙的函数的加和也一定是凸函数,那么可以直接三分找到最值,只不过复杂度为 \(\Theta(n\log^2n)\),好像可以过。

Part2

考虑那个凸函数各部分的斜率,发现越接近答案,斜率越小,可以通过这个直接求出答案拐点的排名。

设答案拐点前有 \(P\) 个点,那么从第 \(P\) 个点到第 \(P+1\) 个点的斜率一定是最小的,单位变化量也是最小的。想象一个点在图像上移动,每移动 \(1\) 的力量值,\(\Delta f(x)=a\times P-b\times(i-P) \leqslant 0\),解得 \(P \leqslant \dfrac{b\times i}{a+b}\), 这个 \(P\) 一定是最大的,所以取等,答案拐点的排名即为 \(P+1\)。

Part3

代码实现上,先离散化,因为要动态查询排名为 \(K\) 的值,快速求出以 \(x\) 为最终的力量值的总代价,所以直接权值线段树就行了,然后一个一个地加,一个一个地求,复杂度 \(\Theta(n\log n)\)。

Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <algorithm>

#define int long long

const int N=1e6+10;

using namespace std;

inline int read() {
    int f=1, x=0;
    char ch=getchar();
    while(ch>'9' || ch<'0') {
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0' && ch<='9') {
        x=(x<<3)+(x<<1)+(ch^48);
        ch=getchar();
    }
    return f*x;
}

inline void write(int x) {
    cout<<x; putchar('\n');
}

int n, a, b;
int d[N], mp[N], ans[N];

map <int,int> Kth;

 struct Tree {
        int sum, size;
    }t[N<<2];

struct Seg_Tree {

    inline Tree merge(Tree ls, Tree rs) {
        Tree res=(Tree){0, 0};
        res.size=ls.size+rs.size;
        res.sum=ls.sum+rs.sum;
        return res;
    }
   
    inline void push_up(int k) {
        t[k]=merge(t[k<<1], t[k<<1|1]);
    }

    void change(int k,int l,int r,int pos,int val) {
        if(l==r) {
            t[k].sum+=val*mp[l];
            t[k].size+=val;
            return;
        }
        int mid=(l+r)>>1;
        if(pos<=mid) change(k<<1, l, mid, pos, val);
        else change(k<<1|1, mid+1, r, pos, val);
        push_up(k);
    }

    int find(int k,int l,int r,int K) {
        if(l==r) return l;
        int mid=(l+r)>>1;
        if(t[k<<1].size>=K) return find(k<<1, l, mid, K);
        else return find(k<<1|1, mid+1, r, K-t[k<<1].size);
    }

    Tree query(int k,int l,int r,int L,int R) {
        if(L<=l && r<=R) return t[k];
        int mid=(l+r)>>1;
        Tree ls=(Tree){0, 0}, rs=(Tree){0, 0};
        if(L<=mid) ls=query(k<<1, l, mid, L, R);
        if(R>mid) rs=query(k<<1|1, mid+1, r, L, R);
        return merge(ls, rs);
    }
}T;

signed main() {

    n=read(), a=read(), b=read();
    for(int i=1;i<=n;i++) {
        d[i]=read();
        mp[i]=d[i];
    }

    sort(mp+1, mp+n+1);
    int Len=unique(mp+1, mp+n+1)-mp-1;
    for(int i=1;i<=Len;i++) Kth[mp[i]]=i;

    for(int i=1;i<=n;i++) {
        T.change(1, 1, n, Kth[d[i]], 1);
        int p=(b*i)/(a+b);
        int pos=T.find(1, 1, n ,p+1);
        Tree ll=(Tree){0, 0}, rr=(Tree){0, 0};
        if(pos-1>=1) ll=T.query(1, 1, n, 1, pos-1);
        if(pos+1<=n) rr=T.query(1, 1, n ,pos+1, n);
        ans[i]=(mp[pos]*ll.size-ll.sum)*a+(rr.sum-mp[pos]*rr.size)*b;
    }
    
    for(int i=1;i<=n;i++) write(ans[i]);

    return 0;
}

T4

\(T4\) 的难度有点水吧?

Part1

根据初中数学知识:

\(a^x·a^y=a^{x+y}\)
\((a_1+a_2+\dots+a_n)·(b_1+b_2+\dots+b_m)=a_1b_1+a_1b_2+\dots+a_1b_m+a_2b_1+a_2b_2+\dots+a_nb_m\)

那么可以直接把贡献拆开,\(u\) 的一部分,\(v\) 的一部分,\(u\) 和 \(v\) 之间的路径。最后直接乘起来就行了。

Part2

设 \(f_i=\sum_{u\in sutree_i}2^{dis_{u,i}}\),$ g_i=\sum_{u=1}^{n}dis_{u,i}\(。\)f_i$ 就是它的子树内的点对他的贡献,\(g_i\) 是所有点对它的贡献。

分为两种情况:

第一种情况,\(u\) 和 \(v\) 都不是 \(lca\),那么经过 \(u\) 和 \(v\) 的路径的两个端点一定一个在 \(u\) 的子树,一个在 \(v\) 的子树,那么答案就是 \(f_u\times f_v \times 2^{dis_u,v}\),这个好算。

第二种情况,\(u\) 和 \(v\) 其中有一个是 \(lca\),默认非 \(lca\) 的那个点为 \(u\),那么经过 \(u\) 和 \(v\) 的路径的两个端点,一个在 \(u\) 子树内,另一个在 把 \(u\) 所在的 \(lca\) 的子树的所有节点刨除后,剩下的其他节点上,画一画图就明白了,我们把 \(u\) 所在的 \(lca\) 的子树的根节点称为 \(clca\),那么答案就是 \(f_u \times (g_{lca}-2\times f_{clca} )\times 2^{dis_{u,lca}}\)。

Part3

实现上,可以用 换根\(dp\) 在 \(\Theta(n)\) 复杂度内求出 \(f_i\) 和 \(g_i\)。(也可以用点分治,只不过复杂度劣一些,\(\Theta(n\log n)\) 加上大常数,可能过不去,而且还相对难写)

求 \(lca\) 和 \(clca\) 可以用树剖(倍增常数太大),也可以用 \(Tarjan\) 离线求。

如何用树剖求 \(clca\),先找到 \(lca\),然后从 \(u\) 往 \(lca\) 跳,记录当前链的链顶,跳到链顶相同后开始分讨。

  • \(u=lca\),也就是上一条链的链顶的父亲是 \(lca\),\(clca\) 和 \(lca\) 不在同一条重链上,那么上一条链的链顶就是 \(clca\)。

  • 剩下的情况就是 \(clca\) 和 \(lca\) 在同一条重链上,因为重链上的 \(dfs\)序 是连续的,那么 \(clca\) 的 \(dfs\)序 就是 \(lca\) 的下一位。预处理时记录一下 \(dfs\)序 就可以了。

复杂度为 \(\Theta(q\log n)\),轻微卡常。

Code
#include <iostream>
#include <cstdio>

#define rint register int
#define ll long long

const int N=1e6+10;
const ll mod=998244353;

using namespace std;

inline int read() {
    int f=1, x=0;
    char ch=getchar();
    while(ch>'9' || ch<'0') {
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0' && ch<='9') {
        x=(x<<3)+(x<<1)+(ch^48);
        ch=getchar();
    }
    return f*x;
}

inline void write(ll x) {
    cout<<x; putchar('\n');
} 

int n, cnt_edge;
struct edge {
    int next ,to;
}e[N<<1];
int head[N];

inline void add_edge(int u,int v) {
    e[++cnt_edge].to=v;
    e[cnt_edge].next=head[u];
    head[u]=cnt_edge;
}

ll vs[N], vr[N];
void dfs1(int now,int fa) {
    vs[now]=1;
    for(rint i=head[now];i;i=e[i].next) {
        int v=e[i].to;
        if(v==fa) continue;
        dfs1(v, now);
        vs[now]=(vs[now]+vs[v]*2ll)%mod;
    }
}

void dfs2(int now,int fa) {
    ll sum=(vr[fa]+mod-vs[now]*2ll)%mod;
    vr[now]=(vs[now]+sum*2ll)%mod;
    for(rint i=head[now];i;i=e[i].next) {
        int v=e[i].to;
        if(v==fa) continue;
        dfs2(v, now);
    }
}

int tim, dfn[N], mp[N];
int dep[N], siz[N], fa[N], top[N], son[N];
struct Heavy {
    void dfs1(int now,int father) {
        dep[now]=dep[father]+1, siz[now]=1;
        fa[now]=father, son[now]=0;
        int maxson=-1;
        for(rint i=head[now];i;i=e[i].next) {
            int v=e[i].to;
            if(v==father) continue;
            dfs1(v, now);
            siz[now]+=siz[v];
            if(siz[v]>maxson) {
                maxson=siz[v], son[now]=v;
            }
        }
    }
    void dfs2(int now,int topf) {
        dfn[now]=++tim, mp[tim]=now;
        top[now]=topf;
        if(son[now]) dfs2(son[now], topf);
        for(rint i=head[now];i;i=e[i].next) {
            int v=e[i].to;
            if(v==son[now] || v==fa[now]) continue;
            dfs2(v, v);
        }
    }
   
    inline int LCA(int x,int y) {
        while(top[x]!=top[y]) {
            if(dep[top[x]] < dep[top[y]]) x^=y, y^=x, x^=y;
            x=fa[top[x]];
        }
        if(dep[x] > dep[y]) x^=y, y^=x, x^=y;
        return x;
    }

    inline int CLCA(int x,int y) {
        int topf=0;
        while(top[x]!=top[y]) {
            topf=top[x];
            x=fa[top[x]];
        }
        if(x==y) return topf;
        else if(fa[x]==y) return x;
        else return mp[dfn[y]+1];
    }
}H;

ll power[N];
void prework() {
    dfs1(1, 0);
    vr[1]=vs[1];
    for(rint i=head[1];i;i=e[i].next) {
        int v=e[i].to;
        dfs2(v, 1);
    }
    power[0]=1;
    for(rint i=1;i<=n+5;++i) {
        power[i]=power[i-1]*2ll%mod;
    }
    H.dfs1(1, 0);
    H.dfs2(1, 1);
}

signed main() {
    
    n=read();
    for(rint i=1;i<n;++i) {
        int u=read(), v=read();
        add_edge(u, v);
        add_edge(v, u);
    }

    prework();

    int q=read();
    for(rint tur=1;tur<=q;++tur) {
        int u=read(), v=read();
        int lca=H.LCA(u, v);
        int dis=dep[u]+dep[v]-2ll*dep[lca];
        ll ans=power[dis];
        if(u==lca || v==lca) {
            if(u==lca) u=v;
            int clca=H.CLCA(u, lca);
            ans=(ans*vs[u]%mod*((vr[lca]-vs[clca]*2ll+mod)%mod))%mod;
        }
        else ans=ans*vs[u]%mod*vs[v]%mod;
        write((ans+mod)%mod);
    }
    
    return 0;
}

标签:now,ch,int,top,30,联测,lca,NOIP2023,include
From: https://www.cnblogs.com/cwymp/p/17807998.html

相关文章

  • MAX7219点阵屏四合一—基于CH32V307的应用
    参考链接:https://blog.csdn.net/weixin_46957846/article/details/127352759本篇文章为基于CH32V307的MAX7219级联应用,代码是基于上参考链接代码基础上修改,若有侵权请联系及时删除。该应用测试所用模块为一个四级级联模块,参考链接代码下载应用4个点阵模块均显示均显示同一个字符......
  • nginx 302问题
    nginx抓包显示302访问的ip端口有发生变化踩坑需配置location/abc{proxy_passhttp://192.168.146.64:7118/;proxy_intercept_errorson;#捕捉错误error_page301302307=@handle_redirects;}......
  • 揭秘!自动化测试效率提升30%如何达成
    揭秘!自动化测试效率提升30%如何达成 一个全新的应用需要经过需求设计、应用开发、应用测试,及应用上架等几个阶段之后,才能到达用户手中。在应用测试中,测试的类型根据不同的开展时机,可以分为单元测试、集成测试、专项测试,以及上架测试。单元测试指对软件中的最小可测试单元进行验证,......
  • NOIP2023模拟9联测30 T4 金牌
    NOIP2023模拟9联测30T4金牌LCA还能\(O(1)\)……思路思路非常简单,可考试就是想歪成统计指数了……将一条穿过\((x,y)\)的路径\((u,v)\)分为\(u\tox\toy\tov\),所以说对答案的贡献为:\[2^{dis(u,x)+dis(x,y)+dis(y,v)}=2^{dis(u,x)}\times2^{dis(x,y)}\times2^{......
  • NOIP2023模拟9联测30 T3 高爸
    NOIP2023模拟9联测30T3高爸三分啊,三分……思路设现在的平均力量值为\(x\),大于\(x\)力量值的龙有\(n\)条,小于等于的龙有\(m\)条,花费为:\[a(n\timesx-\sum_{i=1}^{n+m}p_i(p_i>x))+b(\sum_{i=1}^{n+m}p_i(p_i\leqx)-m\timesx)\]对于\(a(n\timesx-\sum_{......
  • 学习笔记8——20211303ltc
    学习笔记8一、作业要求自学教材第5章,提交学习笔记(10分),评分标准如下1.知识点归纳以及自己最有收获的内容,选择至少2个知识点利用chatgpt等工具进行苏格拉底挑战,并提交过程截图,提示过程参考下面内容(4分)“我在学***X知识点,请你以苏格拉底的方式对我进行提问,一次一个问题”核......
  • NOIP2023 游记
    破釜沉舟了。最前線飛ばせ僕たちは,星もない夜ただ東を目指して行くDAY-16(11.1)B站6级号了。(仅仅靠点赞投币的经验攒出来的,可见我有多摆烂)DAY-15(11.2)打得最逆天的一场正睿,T1做了快2个小时才过,T2简单容斥题想了一个多小时不会。最后一小时打完T2T4暴力,把T3一眼秒了,写了......
  • 10.30
    今天实现了对于学生个人信息添加的基本功能,我使用的是springboot实现后端的代码,通过springboot加mybatis实现接口类的实现。POJO包定义类变量以及返回值变量1、PersonInformation.javapackagecom.example.pojo;importlombok.AllArgsConstructor;importlombok.Data;imp......
  • 揭秘!自动化测试效率提升30%如何达成
      一个全新的应用需要经过需求设计、应用开发、应用测试,及应用上架等几个阶段之后,才能到达用户手中。在应用测试中,测试的类型根据不同的开展时机,可以分为单元测试、集成测试、专项测试,以及上架测试。单元测试指对软件中的最小可测试单元进行验证,围绕函数、类、方法等展开,大......
  • 喜讯!INFINI Easysearch 在墨天轮数据库排名中挺进前30!
    近日,2023年10月的墨天轮中国数据库流行度排行火热出炉,本月共有283个数据库参与排名,中国数据库行业竞争日益激烈。其中,极限科技旗下软件产品INFINIEasysearch稳步推进,在国内整个数据库排行中进入了前30的行列!同时在搜索型数据库分类排名中保持领先,稳住了第一名的......