hgoi#20190706

更好的阅读体验

我的博客观看

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
",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
",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
",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