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]+i∈son∑dp[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]=i∈son∑dp[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 1∼n顺次连接的仙人掌,问这个仙人掌的最大边数
分析
首先有 n − 1 n-1 n−1条边是必须的,设 f [ i ] f[i] f[i]表示 1 ∼ i 1\sim i 1∼i所组成的仙人掌所能含的最大边数,那么 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[i−1],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);
}