T1
Description
“封印大典启动,请出Nescafe魂珠!”随着圣主applepi一声令下,圣剑护法rainbow和魔杖护法freda将Nescafe魂珠放置于封印台上。封印台是一个树形的结构,魂珠放置的位置就是根节点(编号为0)。还有n个其他节点(编号1-n)上放置着封印石,编号为i的封印石需要从魂珠上获取Ei的能量。能量只能沿着树边从魂珠传向封印石,每条边有一个能够传递的能量上限Wi,魂珠的能量是无穷大的。作为封印开始前的准备工作,请你求出最多能满足多少颗封印台的能量需求?
注意:能量可以经过一个节点,不满足它的需求而传向下一个节点。每条边仅能传递一次能量。
Input
第一行一个整数n,表示除根节点之外的其他节点的数量。
接下来n行,第i+1行有三个整数Fi、Ei、Wi,分别表示i号节点的父节点、i号节点上封印石的能量需求、连接节点i与Fi的边最多能传递多少能量。
Output
最多能满足多少颗封印石的能量需求。
思路:
贪心
我们先以每个点的能量需求按小到大排序
那么我们就可以枚举每一个点能否走到根,求出此路径
如果能走到就将当前路径上的所有边减去能量所需
代码:
#include
#include
#include
using namespace std;
int n,ans,f[1010],w[1010];
struct tree{int e,b;}k[1010];
bool cmp(tree a,tree b) { return a.eint main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d%d%d",&f[i],&k[i].e,&w[i]);
k[i].b=i;
}
sort(k+1,k+n+1,cmp);
int j;
for (int i=1;i<=n;i++)
{
for (j=k[i].b;j;j=f[j]) if (w[j]break;
if (j) continue;
for (j=k[i].b;j;j=f[j]) w[j]-=k[i].e;
ans++;
}
printf("%d",ans);
return 0;
}
T2
Description
“圣主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,使得封印获得的总能量尽可能高。为了封印的最后一击尽量完美,就请你写个程序帮他计算一下吧!
Input
第一行一个整数N。
接下来N行每行两个整数ai、bi,第i+1行表示第i种元素的封印区间。
Output
两个用空格隔开的证书,第一个数十能够获得最多总能量的封印力度E,第二个数是获得的总能量大小。当存在多个E能够获得最多总能量时,输出最小的E。
思路:
首先,我们可以将左端点和右端点都压成一个点,并记录它为左端点还是右端点
那么我们一开始记录一个左端点的前缀和
再记录一个m,为有多少个区间中包含这个数(由于数是从小到大排的,所以不用每次跑O(n))
那么如果当前枚举到的点是左端点,那么将前缀和-al,m++,如果取这个数的为al+m*k[i].v(为这个数的值),看能不能更新答案
如果为右端点,类似于上面,不过前缀和不变,m–(就代表当前这个区间没有值了,因为当前枚举的值大于此区间),再求取这个数的价值,看能不能更新答案
代码:
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include
#include
#include
#include
using namespace std;
const int maxn=100005;
struct qj{int v,l;}k[maxn*2];
bool cmp(qj a,qj b){return a.vb.l;}
int a,b,n,ansn;
long long al,m,ans;
void init()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&a,&b);
k[i*2-1].v=a;k[i*2-1].l=1;
k[i*2].v=b;k[i*2].l=2;
al=al+a;
}
}
int main()
{
init();
sort(k+1,k+1+2*n,cmp);
for(int i=1;i<=2*n;i++)
{
long long sum=0;
if (k[i].l==1)
{
al-=k[i].v;
m++;
sum=al+m*k[i].v;
if (sum>ans)
{
ansn=k[i].v;
ans=sum;
}
}
else
{
sum=al+m*k[i].v;
if (sum>ans)
{
ansn=k[i].v;
ans=sum;
}
m--;
}
}
printf("%d %lld",ansn,ans);
return 0;
}
T3
Description
Input
第一行为字符串A。
第二行为字符串B。
Output
输出在B的所有循环同构串中,有多少个能够与A匹配。
思路:
hash
首先按’*’把A串分成几段,从前往后第i段给一个hash值i。
F[i,j]表示在B串的第i个位置之后,第一次出现hash值为j的串的位置。
那么怎么求出所有的F[i,j]呢?
我们枚举1~M*2-1个位置开始,向后求出于a同长的字符串的hash值,看有没有相等的
把B串前M-1个字符复制一份接在B串后面,枚举起始位置1~M,然后利用F[i,j],依次向后找A串的那些段最早出现的位置,判断最后到达的位置和起始位置的差是否超过了M即可。但是要注意如果A串开头、结尾不是’*’,要先把开头结尾处理掉。
代码:
#include
#include
#include;
#include
using namespace std;
unsigned long long k[110],hash[110],g[110],temp;
char a[110],b[200010];
int f[200010][52],e[110],len1,len2,l,w,ans,cd;
int main()
{
scanf("%s",a+1);
len1=strlen(a+1);
hash[0]=1;
for (int i=1;i<=len1;i++) hash[i]=hash[i-1]*13331;
for (int i=1;i<=len1;i++)
if (a[i]=='*')
{
if (i>1&&a[i-1]!='*')
{
k[++l]=temp;
e[l]=cd;
temp=cd=0;
}
}
else
{
temp+=(a[i]-'a'+1)*hash[cd++];
}
if (a[len1]!='*')
{
k[++l]=temp;
e[l]=cd;
temp=cd=0;
}
scanf("%s",b+1);
len2=strlen(b+1);
if (!l)
{
printf("%d",len2);
return 0;
}
for (int i=1;i<=len2;i++) b[len2+i]=b[i];
for (int i=1;i<=l;i++) f[len2*2+1][i]=f[len2*2+2][i]=len2*2+1;
for (int i=len2*2;i;i--)
{
memset(g,-1,sizeof(g));
g[0]=0;
for (int j=0;jif (i+j<=2*len2) g[j+1]=g[j]+(b[i+j]-'a'+1)*hash[j];
for (int j=1;j<=l;j++)
{
f[i][j]=f[i+1][j];
if (g[e[j]]==k[j]) f[i][j]=i+e[j]-1;
}
}
int j,p;
for (int i=1;i<=len2;i++)
{
bool flag=true;
for (j=len1;j&&a[j]!='*';j--) if (a[j]!=b[i+len2-1-(len1-j)]) {flag=false; break;}
if (flag==false||j==0&&len1!=len2) continue;
for (p=1,w=i;p<=l-(a[len1]!='*');p++)
{
if (p==1&&a[1]!='*'&&f[i][p]+1!=i+e[1]) break;
w=f[w][p]+1;
}
if (p>l-(a[len1]!='*')&&w<=i+len2-(len1-j)) ans++;
}
printf("%d",ans);
return 0;
}