hgoi#20190706

本文探讨了质因数计算及字符串编码问题的算法解决方案,包括欧拉筛法求质因数数量,以及DP算法解决特定字符串编码计数问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

更好的阅读体验

我的博客观看

T1-质因数

有一个正整数数列a1,a2…an。定义函数f(x)为x 的不同的质因数数量。
求f(a1),f(a2)…f(an)。

解法

写个欧拉筛,然后欧拉筛的时候直接判断一下有没有新增质因数

ac代码

#include<bits/stdc++.h>
using namespace std;
int n,cnt,x,a[1000010],p[1000010],dp[1000010];
void oula()
{
    for(int i=2;i<=1000000;i++)
    {
        if(a[i]==0)a[i]=p[++cnt]=i,dp[i]=1;
        for(int j=1;j<=cnt;j++)
        {
            if(i*p[j]>1000000||a[i]<p[j])break;
            a[i*p[j]]=p[j];
            if(i%p[j]==0)dp[i*p[j]]=dp[i];
            else dp[i*p[j]]=dp[i]+1;
        }
    }
}
int main()
{
    freopen("easy.in","r",stdin),freopen("easy.out","w",stdout),oula(),scanf("%d",&n);
    while(n--)scanf("%d",&x),printf("%d\n",dp[x]);
    return 0;
} 

T2-编码

一个字符串str的p型编码a的定义如下:把str表示成b1个c1,b2个c2...bn个cn,然后将b1,c1,b2,c2,...,bn,cn首尾拼接成的字符串中最短的字符串设为a。例如:字符串122344111可被描述为"1个1、2个2、1个3、2个4、3个1",因此我们说122344111的p型编码为1122132431。
类似的道理,00000000000可描述为"11个0",因此它的p型编码为110;100200300可描述为"1个1、2个0、1个2、2个0、1个3、2个0",因此它的p型编码为112012201320。
很显然,一个串str的p型编码是固定的,但是可能有多个串的p型编码相同。现在已知一个字符串str的p型编码a,但不知道原来的字符串,求有多少种字符串的p型编码为a。

解法

显然是个dp的题目,我们设状态dp[i]为以第i个字符为结尾的b[x]的方案数,那么最终的答案就是dp[n-1]
我们想状态转移,dp[i]可以从dp[j] (1<=j<=i-2)推的,顺便一提,初值都是1
问题是无法转移的情况,一种是a[i]为0时,不能从dp[i-2]转移至dp[i]
因为dp[i-2]的时候一个b[x]结尾了,a[i-1]是c[x],b[i+1]就是a[i]
而题目要求的是最短字符串,所以不会有前导0,所以这种情况不行
另一种情况,如果a[j+1]==a[i+1],就一定能合并,而不是最短的,所以也不能转移
我们只需要维护一个sum以及各种c[x]的方案数即可

ac代码

#include<bits/stdc++.h>
#define mod 998244353
using namespace std;
char s[1000010];
int n,sum,ss[10],a[1000010],dp[1000010];
int main()
{
    freopen("hard.in","r",stdin),freopen("hard.out","w",stdout),scanf("%s",s),n=strlen(s);
    for(int i=1;i<=n;i++)a[i]=s[i-1]-'0',dp[i]=1;
    for(int i=3;i<n;i++)
    {
        if(a[i]!=0)sum=(sum+dp[i-2])%mod,ss[a[i-1]]=(ss[a[i-1]]+dp[i-2])%mod;
        dp[i]=((dp[i]+sum-ss[a[i+1]])%mod+mod)%mod;
    }
    if(a[1]==0)puts("0");else printf("%d\n",dp[n-1]);
    return 0;
}

T3-八卦阵

有一个八卦阵。这个阵可以认为是一个n个点,m条边的有向图。每条边i有一个困难a[i]。每个点u有八个状态:b[u][1],b[u][2],...,b[u][8],这八个状态均为[1,8]之间的整数(不一定是排列),在第i 秒中,u的状态为b[u][(i-1)%8+1]。对于任意两个状态i,j都存在一个复杂度c[i][j]。
现在想从1号点走到n号点,在第一秒开始时在1号点,每次可以用1秒从当前点u走到另一个有边相连的点v。也就是说,在第一秒内走过第一条边,在第二秒内走过第二条边……由于八卦阵极其巧妙,必须砸出一些榔头才能走过一条边。若在第i秒通过第j条边从点u走到点v,需要砸出a[j]+c[b[u][(i-1)%8+1]][b[v][(i-1)%8+1]]个榔头。
请计算出至少要多少个榔头才能从点1走到点n。

解法

这道题挺水的,感觉还没T2难,只要跑最短路就好了,把一个点剖成八个点,然后跑最短路
考虑到会被卡spfa,优先队列优化一下,不多说咯QWQ

ac代码

#include<bits/stdc++.h>
#define inf (2e9)
#define v e[i].to
#define d e[i].w+c[b[u.nw][u.t]][b[v][u.t]]
using namespace std;
struct node{int to,w,next;}e[300010];
int n,m,cnt,x,y,z,head[100010],c[10][10],b[100010][10],dis[100010][10],used[100010][10];
struct Node{int nw,t,dd;bool operator<(const Node b)const{return dd>b.dd;}}u;
priority_queue<Node>q;
void add(){e[++cnt]={y,z,head[x]},head[x]=cnt;}
void spfa()
{
    while(!q.empty())
    {
        while(used[q.top().nw][q.top().t])q.pop();
        u=q.top(),q.pop(),used[u.nw][u.t]=1;
        if(u.nw==n)printf("%d\n",dis[u.nw][u.t]),exit(0);
        for(int i=head[u.nw];i;i=e[i].next)
            if(!used[v][u.t%8+1])if(u.dd+d<dis[v][u.t%8+1])
                dis[v][u.t%8+1]=u.dd+d,q.push({v,u.t%8+1,dis[v][u.t%8+1]});
    }
}
int main()
{
    freopen("hammer.in","r",stdin),freopen("hammer.out","w",stdout),scanf("%d%d",&n,&m);
    for(int i=1;i<=8;i++)for(int j=1;j<=8;j++)scanf("%d",&c[i][j]);
    for(int i=1;i<=n;i++)for(int j=1;j<=8;j++)scanf("%d",&b[i][j]),dis[i][j]=inf;
    for(int i=1;i<=m;i++)scanf("%d%d%d",&x,&y,&z),add();
    dis[1][1]=0,q.push({1,1,0}),spfa();
    return 0;
}

转载于:https://www.cnblogs.com/muronglin/p/hgoi-20190706.html

# P8482 「HGOI-1」Number ## 题目背景 $\text{bh1234666}$ 正在学习乘法! ## 题目描述 $\text{bh1234666}$ 有一定数量的数字 $0 \sim 9$,现在他想让你寻找一种分配方案,将它们分成两个整数,使得他们的乘积 $p$ 最大。 由于 $\text{bh1234666}$ 不喜欢太大的数,所以你只需要输出**两个非负整数**,使它们的乘积**等于**最大乘积 $p$,但是这两个整数 $0 \sim 9$ 的数量不能等于给定的数量(任意一个数字数量不相等即可,**不考虑前导零**)。 $\text{bh1234666}$ 是很善良的,如果 $0 \sim 9$ 的数量等于给定的数量了,你依旧可以得到的一半的分。 ## 输入格式 第一行十个整数 $c_0,c_1,\cdots c_9$,分别表示 $0 \sim 9$ 的个数。 ## 输出格式 共两行每行一个非负整数,分别表示你给出的两个非负整数。 ## 输入输出样例 #1 ### 输入 #1 ``` 1 2 3 2 1 1 2 1 2 1 ``` ### 输出 #1 ``` 13949030 620572547 ``` ## 说明/提示 #### 样例解释 最大可能乘积为 $97643210 \times 88653221=13949030 \times 620572547=8656385075279410$。 若输出 $97643210 \times 88653221$ 则只能得到一半的分,因为 $0\sim 9$ 出现的次数与给定的相同。 #### 数据范围及约定 本题采用**捆绑测试**,共有 $5$ 个 $\text{subtask}$,最终分数为所有 $\text{subtask}$ 分数之和。 $$ \def\arraystretch{1.5} \begin{array}{|c|c|c|}\hline \textbf{Task} & \textbf{Score} & \sum c_i\le \cr\hline 1 & 10 & 20 \cr\hline 2 & 20 & 100 \cr\hline 3 & 20 & 5000 \cr\hline 4 & 20 & 10^6 \cr\hline 5 & 30 & 10^7 \cr\hline \end{array} $$ 对于 $100\%$ 的数据,保证 $1 \le c_i$,$\sum c_i \le 10^7$。 #### 说明 本题有 $\text{spj}$,两数乘积正确得一半的分,数量与给出的不同且乘积正确得全部分数。故每一 $\text{subtask}$ 的得分为其中所有数据点得分的**最小值**。
07-09
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值