能量获取
一、题目
“封印大典启动,请出Nescafe魂珠!”随着圣主applepi一声令下,圣剑护法rainbow和魔杖护法freda将Nescafe魂珠放置于封印台上。封印台是一个树形的结构,魂珠放置的位置就是根节点(编号为0)。还有n个其他节点(编号1-n)上放置着封印石,编号为i的封印石需要从魂珠上获取Ei的能量。能量只能沿着树边从魂珠传向封印石,每条边有一个能够传递的能量上限Wi,魂珠的能量是无穷大的。作为封印开始前的准备工作,请你求出最多能满足多少颗封印台的能量需求?
注意:能量可以经过一个节点,不满足它的需求而传向下一个节点。每条边仅能传递一次能量。
二、解法
0x01 树形dp
我觉得大家应该都想的出来,直接跑背包,
O
(
n
m
2
)
O(nm^{2})
O(nm2)
0x02 贪心
排序后选消耗最小的点,把它到根的路径全部减去他的消耗,贡献答案,用树链剖分维护可以做到
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。
#include <cstdio>
#include <iostream>
using namespace std;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,tot,E[1005],dp[1005][105],f[1005],ans;
struct edge
{
int v,c,next;
}e[2005];
void dfs(int u,int fa,int pre)
{
for(int i=E[u];i<=pre;i++)
dp[u][i]=1;
for(int i=f[u];i;i=e[i].next)
{
int v=e[i].v,c=e[i].c;
if(v==fa) continue;
dfs(v,u,c);
if(u)
for(int j=pre;j>=0;j--)
for(int k=0;k<=j && k<=c;k++)
dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]);
else
ans+=dp[v][c];
}
}
int main()
{
n=read();
for(int i=1,j,c;i<=n;i++)
{
j=read(),E[i]=read(),c=read();
e[++tot]=edge{j,c,f[i]},f[i]=tot;
e[++tot]=edge{i,c,f[j]},f[j]=tot;
}
dfs(0,0,0);
printf("%d\n",ans);
}
封印一击
一、题目
“圣主applepi于公元2011年9月创造了Nescafe,它在散发了16吃光辉之后与公元2011年11月12日被封印为一颗魂珠,贮藏于Nescafe神塔之中。公元2012年9月,圣主带领四大护法重启了Nescafe,如今已经是Nescafe之魂的第30吃传播了。不久,它就要被第二次封印,而变成一座神杯。。。”applepi思索着Nescafe的历史,准备着第二次封印。
Nescafe由n种元素组成(编号为1~n),第i种元素有一个封印区[ai,bi]。当封印力度E小于ai时,该元素获得ai的封印能量;当封印力度E在ai到bi之间时,该元素将获得E的封印能量;而当封印力度E大于bi时,该元素将被破坏从而不能获得任何封印能量。现在圣主applepi想选择恰当的E,使得封印获得的总能量尽可能高。为了封印的最后一击尽量完美,就请你写个程序帮他计算一下吧!
二、解法
贪心地看,答案一定是某个区间的右端点。
我们枚举所有又端点,发现可以将每个右端点对应的答案分成三部分。
1、
a
a
a大于
b
[
i
]
b[i]
b[i]的,可以排序后
u
p
p
e
r
_
b
o
u
n
d
upper\_bound
upper_bound求出范围,前缀和处理即可。
2、
b
b
b小于
b
[
i
]
b[i]
b[i]的,可以排序后
l
o
w
e
r
_
b
o
u
n
d
−
1
lower\_bound-1
lower_bound−1求出范围,不去计算。
3、
a
≤
b
[
i
]
≤
b
a\leq b[i]\leq b
a≤b[i]≤b的,减去前两个的个数,乘上
b
[
i
]
b[i]
b[i]即可。
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 100005;
#define int long long
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,Max,ans,a[MAXN],b[MAXN],pre[MAXN];
signed main()
{
n=read();
for(int i=1;i<=n;i++)
a[i]=read(),b[i]=read();
sort(a+1,a+1+n);
sort(b+1,b+1+n);
for(int i=1;i<=n;i++)
pre[i]=pre[i-1]+a[i];
for(int i=1;i<=n;i++)
{
int up=upper_bound(a+1,a+1+n,b[i])-a;
int dow=lower_bound(b+1,b+1+n,b[i])-b-1;
int E=n-(n-up+1)-dow;
int val=pre[n]-pre[up-1]+E*b[i];
if(val>Max)
{
Max=val;
ans=b[i];
}
}
printf("%lld %lld\n",ans,Max);
}
归途与征程
一、题目
给出一个长度为N的由小写字母’a’‘z’和’‘组成的字符串A,一个长度为M的仅由小写字母’a’‘z’组成的字符串B。一个’'可以匹配任意多个字符(包括0个)。求在B的所有循环同构串中,有多少个能够与A匹配。
循环同构串:就是把B的前k个字母(0≤k<M)移到结尾所得到的字符串。例如abc的循环同构串由abc、bca和cab。
A与B匹配:若除了A中的’ * '可以匹配B中的任意多个字符串外,其余字符一一对应,则称A与B匹配。例如abc与aadbc是匹配的,其中第一个对应ad,第二个对应空串。
原题参考:http://codeforces.com/gym/100113 中的G题
二、解法
考试时方法很接近了,统计答案时弄错了
q
w
q
qwq
qwq。
既然是一个环串,我们把原串扩大2倍,枚举起点即可。
把
A
A
A串按星号划分成若干个独立的串,每个串放到
B
B
B中跑
H
a
s
h
Hash
Hash,处理处
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j],即对于第
j
j
j个
A
A
A的独立串在
B
B
B串中匹配成功的距离
i
i
i最近的起点下标(可能有点绕,多读几遍理解一下),这样对于每个独立串,我们就可以直接跳到下一个独立串的匹配。
处理好之后我们来考虑怎么统计答案。我们枚举
B
B
B的起点,一个一个跳即可,我们需要特判掉开头和结尾不是星号的情况,不想多说,看代码吧。
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
#define ull unsigned long long
const int MAXN = 200005;
int read()
{
int x=0,flag=1;char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int n,m,le[105],ans,cnt,dp[MAXN][105];
ull Hash[MAXN],Pow[MAXN]={1};
char a[105],b[MAXN];
ull get_hash(int l,int r)
{
return Hash[r]-Hash[l-1]*Pow[r-l+1];
}
void make(int len,string x)
{
ull H=0;cnt++;
if(x=="") return ;
le[cnt]=len;
for(int i=0;i<len;i++)
H=H*371ll+x[i];
for(int i=1;i<=m-len+1;i++)
{
if(H==get_hash(i,i+len-1))
dp[i][cnt]=i;
}
for(int i=m;i>=1;i--)
if(!dp[i][cnt])
dp[i][cnt]=dp[i+1][cnt];
}
int main()
{
scanf("%s",a+1);
scanf("%s",b+1);
n=strlen(a+1);m=strlen(b+1);
for(int i=1;i<=m;i++)
b[i+m]=b[i];
m<<=1;
for(int i=1;i<=m;i++)
Pow[i]=Pow[i-1]*371ll;
for(int i=1;i<=m;i++)
Hash[i]=Hash[i-1]*371ll+b[i];
for(int i=1;i<=n;i++)
{
string x="";int j=i;
if(a[i]=='*') continue;
for(;j<=n && a[j]^'*';j++)
x+=a[j];
make(j-i,x);
i=j-1;
}
for(int i=1;i<=m/2;i++)
{
int j=n,flag=1,t,l;
for(;j>=1 && a[j]!='*';j--)
if(a[j]!=b[m/2+i-1-(n-j)])
{
flag=0;
break;
}
if(!flag || (j==0 && n!=m/2)) continue;
for(t=1,l=1;t<=cnt-(a[n]!='*');t++)
{
if(t==1 && a[1]^'*' && dp[i][1]^i) break;
l=dp[i][t]+le[t];
}
if(t>cnt-(a[n]!='*') && l<=i+m/2) ans++;
}
printf("%d\n",ans);
}