3.18做题记录

这篇博客探讨了欧拉函数在数论中的作用,以及如何构建和解决虚树问题。文章通过实例展示了如何计算质因子数和森林旅行中的最短路径,并介绍了用于优化这些问题的算法。此外,还提到了在数据结构和图论中处理复杂问题的策略,如最小生成树和区间限制的最短路径问题。
ybtoj913. 「欧拉函数」质因子数 请添加图片描述
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
const ll mod=1e9+7;
int phi[maxn],mu[maxn];
int is[maxn],pr[maxn],cnt;
ll n;
ll add(ll x,ll y)
{
    return (x+y)%mod;
}
void init()
{
    phi[1]=mu[1]=1;
    for(int i=2;i<maxn;i++)
    {
        if(!is[i])
        {
            pr[++cnt]=i;
            phi[i]=i-1;
            mu[i]=-1;
        }
        for(int j=1;j<=cnt && pr[j]*i<maxn;j++)
        {
            is[i*pr[j]]=1;
            if(i%pr[j]==0)
            {
                mu[i*pr[j]]=0;
                phi[i*pr[j]]=phi[i]*pr[j];
                break;
            }
            mu[i*pr[j]]=-mu[i];
            phi[i*pr[j]]=phi[i]*(pr[j]-1);
        }
    }
    for(int i=2;i<maxn;i++) phi[i]=add(phi[i],phi[i-1]);
}
ll solve(ll m)
{
    ll res=0;
    for(int d=1;d<=m;d++)
    {
        if(!mu[d]) continue;
        ll sum=0;
        for(int i=1;i<=m/d;i++)
        {
            ll tmp=n/(1ll*i*d*d);
            if(!tmp) break;
            sum=add(sum,tmp);
        }
        res=add(res,sum*mu[d]%mod+mod);
    }
    return 2*(res-phi[m]+mod)%mod+1;
}
int main()
{
    freopen("prime.in","r",stdin);
    freopen("prime.out","w",stdout);
    int T;
    scanf("%d",&T);
    init();
    // for(int i=2;i<=10;i++) printf("%d %d\n",mu[i],phi[i]);
    while(T--)
    {
        scanf("%lld",&n);
        printf("%lld\n",solve(sqrt(n))%mod);
    }
    return 0;
}

ybtoj721. 「虚树」森林旅行

原题好像出自牛客挑战赛35E
对于每个树建立关键点的虚树,边权改为原树上的距离
关键点指连了增加m条边的或者是有询问的点,然后跑单源最短路即可

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=3e5+5;
const ll inf=1e18;
typedef pair<int,int> PII;
typedef pair<ll,int> PLI;
#define mp make_pair
int n,m,q,bh;
vector <int> G[maxn];
vector <PII> T[maxn<<4];
int dep[maxn],siz[maxn],dfn[maxn],dfstime;
int fa[maxn][21],qs[maxn],qt[maxn];
void dfs(int u,int f)
{
    dep[u]=dep[f]+1; fa[u][0]=f;
    siz[u]=1; dfn[u]=++dfstime;
    for(int i=1;i<=20;i++) fa[u][i]=fa[fa[u][i-1]][i-1];
    for(auto to:G[u])
    {
        if(to==f) continue;
        dfs(to,u);
        siz[u]+=siz[to];
    }
}
int lca(int x,int y)
{
    if(dep[x]<dep[y]) std::swap(x,y);
    for(int i=20;i>=0;i--)
        if(dep[fa[x][i]]>=dep[y])
            x=fa[x][i];
    if(x==y) return x;
    for(int i=20;i>=0;i--)
        if(fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
bool cmp(int x,int y)
{
    return dfn[x]<dfn[y];
}
map <PII,int> id;
void check(PII t)
{
    if(id.find(t)==id.end()) id[t]=++bh;
    return;
}
void add(int x,int y,int z)
{
    // printf("%d %d %d\n",x,y,z);
    T[x].push_back(mp(y,z));
    T[y].push_back(mp(x,z));
}
vector <int> tree[maxn];
int st[maxn],p[maxn],top,cnt;
int fath[maxn];
void build(int now)
{
    int sz=tree[now].size();
    if(!sz) return;
    cnt=0;
    for(auto u:tree[now]) p[++cnt]=u;
    if(id.find(mp(now,1))==id.end()) p[++cnt]=1,sz++;
    sort(p+1,p+cnt+1,cmp);
    top=0; st[0]=0;
    for(int i=1;i<=sz;i++)
    {
        if(!top)
        {
            st[++top]=p[i];
            fath[p[i]]=0;
            continue;
        }
        int u=p[i],x=lca(u,st[top]);
        while(dep[x]<dep[st[top]])
        {
            if(dep[x]>=dep[st[top-1]]) fath[st[top]]=x;
            top--;
        }
        if(x!=st[top])
        {
            p[++cnt]=x;
            fath[x]=st[top];
            st[++top]=x;
        }
        fath[u]=x; st[++top]=u;
    }
    for(int i=1;i<=cnt;i++)
        check(mp(now,p[i]));
    for(int i=1;i<=cnt;i++) 
        if(fath[p[i]])
            add(id[mp(now,p[i])],id[mp(now,fath[p[i]])],dep[p[i]]-dep[fath[p[i]]]);
}
ll dis[maxn<<4];
int vis[maxn<<4];
ll dij(int S,int ed)
{
    for(int i=1;i<=bh;i++) dis[i]=inf,vis[i]=0;
    dis[S]=0;
    priority_queue <PLI> q;
    q.push(mp(0,S));
    while(!q.empty())
    {
        int u=q.top().second;
        q.pop();
        if(vis[u]) continue;
        vis[u]=1;
        for(auto to:T[u])
        {
            int v=to.first;
            if(dis[v]>dis[u]+to.second)
            {
                dis[v]=dis[u]+to.second;
                q.push(mp(-dis[v],v));
            }
        }
    }
    return dis[ed];
}
int main()
{
    freopen("travel.in","r",stdin);
    freopen("travel.out","w",stdout);
    scanf("%d%d%d",&n,&m,&q);
    int x,y;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        G[x].push_back(y);
        G[y].push_back(x);
    }
    dfs(1,0);
    for(int i=1;i<=m;i++)
    {
        int p1,p2,u1,u2;
        scanf("%d%d%d%d",&p1,&u1,&p2,&u2);
        if(id.find(mp(u1,p1))==id.end()) tree[u1].push_back(p1);
        if(id.find(mp(u2,p2))==id.end()) tree[u2].push_back(p2);
        check(mp(u1,p1)); check(mp(u2,p2));
        add(id[mp(u1,p1)],id[mp(u2,p2)],1);
    }
    for(int i=1;i<=q;i++)
    {
        int p1,p2,u1,u2;
        scanf("%d%d%d%d",&p1,&u1,&p2,&u2);
        if(id.find(mp(u1,p1))==id.end()) tree[u1].push_back(p1);
        if(id.find(mp(u2,p2))==id.end()) tree[u2].push_back(p2);
        check(mp(u1,p1)); check(mp(u2,p2));
        qs[i]=id[mp(u1,p1)]; qt[i]=id[mp(u2,p2)];     
    }
    for(int i=1;i<=2e5;i++) 
        build(i);
    // for(int i=1;i<=3;i++)
    //     for(int j=1;j<=3;j++)
    //         printf("id of(%d %d):%d\n",i,j,id[mp(i,j)]);
    for(int i=1;i<=q;i++)
    {
        ll res=dij(qs[i],qt[i]);
        // printf("%d %d\n",qs[i],qt[i]);
        if(res!=inf)
            printf("%lld\n",res);
        else printf("impossible\n");
    }
    return 0;
}

CF1327F AND Segments

对于每一位单独考添加链接描述虑然后乘起来
如果区间内限制为1,意味着所有l-r内的所有位置一定都为1
f i , j f_{i,j} fi,j表示填到前 i 个位置,最后一个为0的位置在 j ,满足目前所有限制的方案数,那么我们就可以写出来转移方程

发现这个转移大部分都沿用了上一部分,舍弃了开始的一些,然后单点修改 i 位置,也就是这个位置决策填0

我们维护一个 l i l_i li表示 i 位置要求做靠右的为0的点是哪里,发现这个 l i l_i li单调,我们可以用一个指针扫过去,加上前缀和,就可以做到均摊复杂度

代码

P3320 [SDOI2015]寻宝游戏

树上经过一些点的最短的距离和,相当于把这些点连起来的最小生成树边权大小,是一个常见的问题,可以把这些点按照dfn排序,相邻两点的距离和的一半就是答案,画画图很容易理解

我们可以用一个set按照点的dfn作为键值维护一下,维护答案即可

代码

P3233 [HNOI2014]世界树

先两个dfs求出每个点受哪个点控制,然后建立虚树,考虑虚树上每个边如果两端是同一个人控制,可以直接加上这两个点儿子大小作为贡献,如果两点不受同一个点控制,可以倍增求出分界点,计算每个点不在虚树上的点的个数作为贡献

代码细节较多

代码

本系统旨在构建一套面向高等院校的综合性教务管理平台,涵盖学生、教师及教务处三个核心角色的业务需求。系统设计着重于实现教学流程的规范化与数据处理的自动化,以提升日常教学管理工作的效率与准确性。 在面向学生的功能模块中,系统提供了课程选修服务,学生可依据培养方案选择相应课程,并生成个人专属的课表。成绩查询功能支持学生查阅个人各科目成绩,同时系统可自动计算并展示该课程的全班最高分、平均分、最低分以及学生在班级内的成绩排名。 教师端功能主要围绕课程与成绩管理展开。教师可发起课程设置申请,提交包括课程编码、课程名称、学分学时、课程概述在内的新课程信息,亦可对已开设课程的信息进行更新或撤销。在课程管理方面,教师具备入所授课程期末考试成绩的权限,并可导出选修该课程的学生名单。 教务处作为管理中枢,拥有课程审批与教学统筹两大核心职能。课程设置审批模块负责处理教师提交的课程申请,管理员可根据教学计划与资源情况进行审核批复。教学安排模块则负责全局管控,包括管理所有学生的选课最终结果、生成包含学号、姓名、课程及成绩的正式成绩单,并能基于选课与成绩数据,统计各门课程的实际选课人数、最高分、最低分、平均分以及成绩合格的学生数量。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值