2019.11.06【NOIP提高组】模拟 B 组

99 篇文章 1 订阅
31 篇文章 0 订阅

JZOJ 3843 寻找羔羊

题目

给定一个由小写字母组成的字符串,寻找包含agnus的子串的个数。注意:当且仅当两个子串的起始位置和终点不同时,这两个子串属于不同的子串。


分析

若找到一个羔羊单词,那么计算左右可扩展的范围,乘起来再求和即可


代码

#include <cstdio>
#include <cstring>
#define rr register
using namespace std;
char s[30011]; int n,ans;
signed main(){
	scanf("%s",s+1);
	n=strlen(s+1); rr int st=0;
	for (rr int i=5;i<=n;++i)
	if (s[i-4]=='a'&&s[i-3]=='g'&&s[i-2]=='n'&&s[i-1]=='u'&&s[i]=='s'){
		ans+=(n-i+1)*(i-4-st);
		st=i-4;
	}
	return !printf("%d",ans);
}

JZOJ 3844 统计损失

题目

求一棵树上任意两点(可以相同)间的边权之积的和


分析

d p [ x ] dp[x] dp[x]表示以 x x x为根的子树所形成一端为 x x x的简单路径的边权之积的和,那么
d p [ x ] = a [ x ] + ∑ i ∈ s o n d p [ i ] ∗ a [ x ] dp[x]=a[x]+\sum_{i\in son}dp[i]*a[x] dp[x]=a[x]+isondp[i]a[x]
f [ x ] f[x] f[x]表示以 x x x为根的子树所形成的两端都不为 x x x的简单路径的边权之积的和,那么
f [ x ] = ∑ i ∈ s o n d p [ s o n ] ( ≠ i ) ∗ d p [ i ] ∗ a [ x ] f[x]=\sum_{i\in son}dp[son](\neq i)*dp[i]*a[x] f[x]=isondp[son](=i)dp[i]a[x]
把两者加起来就是答案


代码

#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int mod=10086,N=100011;
struct node{int y,next;}e[N<<1];
int dp[N],f[N],ls[N],a[N],n,k=1;
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans; 
}
inline signed mo(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline void dfs(int x,int fa){
	rr int sum=0;
	for (rr int i=ls[x];i;i=e[i].next)
	if (e[i].y!=fa){
		dfs(e[i].y,x); rr int now=a[x]*dp[e[i].y]%mod;
		f[x]=mo(f[x],sum*now%mod);
		dp[x]=mo(dp[x],now);
		sum=mo(sum,dp[e[i].y]);
	}
	dp[x]=mo(dp[x],a[x]); 
}
signed main(){
	n=iut();
	for (rr int i=1;i<=n;++i) a[i]=iut()%mod;
	for (rr int i=1;i<n;++i){
		rr int x=iut(),y=iut();
		e[++k]=(node){y,ls[x]},ls[x]=k,
		e[++k]=(node){x,ls[y]},ls[y]=k;
	}
	dfs(1,0); rr int ans=0;
	for (rr int i=1;i<=n;++i) ans=mo(ans,mo(dp[i],f[i]));
	return !printf("%d",ans);
}

JZOJ 3845 简单题

题目

在一个无向图中找到存在一条简单路径使 1 ∼ n 1\sim n 1n顺次连接的仙人掌,问这个仙人掌的最大边数


分析

首先有 n − 1 n-1 n1条边是必须的,设 f [ i ] f[i] f[i]表示 1 ∼ i 1\sim i 1i所组成的仙人掌所能含的最大边数,那么 f [ i ] = m i n ( f [ i − 1 ] , f [ m a x l e f [ i ] ] + 1 ) f[i]=min(f[i-1],f[maxlef[i]]+1) f[i]=min(f[i1],f[maxlef[i]]+1) m a x l e f maxlef maxlef表示某个右端点所拥有的边中最大的左端点


代码

#include <cstdio>
#include <cctype>
#define rr register
using namespace std;
const int N=100011;
int n,m,ans,dp[N],pre[N];
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline void Mx(int &a,int b){a=a>b?a:b;}
signed main(){
	n=iut(); m=iut();
	for (rr int i=1;i<=m;++i){
		rr int x=iut(),y=iut();
		if (x>y) x^=y,y^=x,x^=y;
		if (x+1<y&&pre[y]<x) pre[y]=x;
	}
	for (rr int i=1;i<=n;++i){
	    dp[i]=dp[i-1];
	    if (pre[i]) Mx(dp[i],dp[pre[i]]+1);
    }
    return !printf("%d",dp[n]+n-1);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值