第一题
【NOIP2013模拟】能量获取 (Standard IO)
Time Limits: 1000 ms Memory Limits: 131072 KB Detailed Limits
Goto ProblemSet
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
最多能满足多少颗封印石的能量需求。
Sample Input
4
0 3 2
0 100 100
1 1 1
2 75 80
Sample Output
2
Data Constraint
对于100%的数据,满足1<=n<=1000,0<=Fi<=n,0<=Ei,Wi<=100
题解:每次选取能量需求最小的节点,扫描它到根节点的路径上的边的容量,看能否满足,如果能满足就把它到根节点的路径上的边的容量都减去它的需求即可。
当然有dalao用树形dp过了,但我不会。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int fa[1010],w[1010];
struct hh
{
int e,pos;
}a[1010];
int n,ans;
bool come(hh a,hh b)
{
return a.e<b.e;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%d%d%d",&fa[i],&a[i].e,&w[i]);
a[i].pos=i;
}
sort(a+1,a+n+1,come);
int j;
for(int i=1;i<=n;i++)
{
for(j=a[i].pos;j;j=fa[j])
if(w[j]<a[i].e) break;
if(j) continue;
for(j=a[i].pos;j;j=fa[j])
w[j]-=a[i].e;
ans++;
}
cout<<ans;
return 0;
}
第二题
【NOIP2013模拟】封印一击 (Standard IO)
Time Limits: 1000 ms Memory Limits: 131072 KB Detailed Limits
Goto ProblemSet
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。
Sample Input
2
5 10
20 25
Sample Output
10 30
Data Constraint
对于50%的数据,1<=N<=1000,1<=ai<=bi<=10000。
对于100%的数据,1<=N<=10^5,1<=ai<=bi<=10^9。
题解:很容易想到,最优解的e肯定是某个区间的右端点!
所以我们把所有区间的左右端点取出,从小到大排序,扫描一遍。
维护一个变量sa,表示扫描到的值为p时,左端点大于p的所有区间的左端点之和;维护一个变量sp,表示扫描到的值为p时,p在左右端点之间的区间个数。那么此时可以封印一击得到的能量就是sa+sp*p。
扫描时遇到一个左端点,sa减去这个左端点,sp加一;遇到一个右端点,sp减一即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100010
#define LL long long
struct hh
{
int v,pd;
}c[2*N+2];
int a[N],b[N],n;
LL ans=-1,sa,sp,k;
bool come(hh a,hh b)
{
if(a.v!=b.v) return a.v<b.v;
else return a.pd>b.pd;
}
using namespace std;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i]>>b[i];
sa+=a[i];
c[i].v=a[i];c[i].pd=1;
c[n+i].v=b[i];c[n+i].pd=-1;
}
sort(c+1,c+2*n+1,come);
for(int i=1;i<=2*n;i++)
{
if(c[i].pd==1)
{
sp++;sa-=c[i].v;
}
else
{
if(ans<sa+sp*c[i].v) k=c[i].v,ans=sa+sp*c[i].v;
sp--;
}
}
cout<<k<<" "<<ans;
return 0;
}
第三题
【NOIP2013模拟】归途与征程 (Standard IO)
Time Limits: 1000 ms Memory Limits: 131072 KB
Input
第一行为字符串A。
第二行为字符串B。
Output
输出在B的所有循环同构串中,有多少个能够与A匹配。
Sample Input
输入1:
aaaa
aaaa
输入2:
a*a
aaaaaa
输入3:
a*b*c
abacabadabacaba
Sample Output
输出1:
4
输出2:
6
输出3:
15
Data Constraint
对于30%的数据,M<=20;
对于80%的测试点,M<=200;
对于100%的测试点,1<=N<=100,1<=M<=100000。
那个我还没写出,先贴一下官方的题解啊:
第三题 归途与征程 From lydrainbowcat
类型:动态规划
算法一:类比最长公共子序列的DP方法 (O(NM^2) 80分)
首先枚举B的M个循环同构串。F[i,j]表示A串前i个字符和B串前j个字符能否匹配。
如果a[i]=b[j],F[i,j]=F[i-1,j-1];
如果a[i]='*',F[i,j]=F[i-1,j] or F[i,j-1] or F[i-1,j-1]。
边界:F[0,0]=true,如果a[1]='*',F[1,0]=true,其余为false。
算法二:DP+RKhash (O(NM) 100分)
首先按'*'把A串分成几段,从前往后第i段给一个hash值i,这样的hash值最多有(N+1)/2个。
F[i,j]表示在B串的第i个位置之后,第一次出现hash值为j的串的位置。
把B串前M-1个字符复制一份接在B串后面,枚举起始位置1~M,然后利用F[i,j],依次向后找A串的那些段最早出现的位置,判断最后到达的位置和起始位置的差是否超过了M即可。但是要注意如果A串开头、结尾不是'*',要先把开头结尾处理掉。
本题数据不好做,可能我做的比较弱(本来就只有两个大点)。AC我的数据的程序可能还少处理了很多特殊情况,建议AC的童鞋搜一下CodeForces GYM的Pattern Matching这道题,去CF上提交下。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
unsigned long long c[110],p[110],g[110],temp;
char a[110],b[200010];
int f[200010][52],d[110],n,m,t,i,j,k,l,ans;
int main()
{
freopen("journey.in","r",stdin);
freopen("journey.out","w",stdout);
scanf("%s",a+1);
n=strlen(a+1);
for(p[0]=i=1;i<=n;i++) p[i]=p[i-1]*13331;
for(i=1;i<=n;i++)
if(a[i]=='*')
if(i>1&&a[i-1]!='*') c[++t]=temp,d[t]=j,temp=j=0; else;
else temp+=(a[i]-'a'+1)*p[j++];
if(a[n]!='*') c[++t]=temp,d[t]=j,temp=j=0;
scanf("%s",b+1);
m=strlen(b+1);
if(!t) {cout<<m<<endl; return 0;}
for(i=1;i<=m;i++) b[m+i]=b[i];
for(j=1;j<=t;j++) f[m*2+1][j]=f[m*2+2][j]=m*2+1;
for(i=m*2;i;i--)
{
memset(g,-1,sizeof(g));
g[0]=0;
for(j=0;j<n;j++)
if(i+j<=2*m) g[j+1]=g[j]+(b[i+j]-'a'+1)*p[j];
for(j=1;j<=t;j++)
{
f[i][j]=f[i+1][j];
if(g[d[j]]==c[j]) f[i][j]=i+d[j]-1;
}
}
for(i=1;i<=m;i++)
{
bool flag=true;
for(j=n;j&&a[j]!='*';j--)
if(a[j]!=b[i+m-1-(n-j)]) {flag=false; break;}
if(!flag||j==0&&n!=m) continue;
for(k=1,l=i;k<=t-(a[n]!='*');k++)
{
if(k==1&&a[1]!='*'&&f[i][k]+1!=i+d[1]) break;
l=f[l][k]+1;
}
if(k>t-(a[n]!='*')&&l<=i+m-(n-j)) ans++;
}
cout<<ans<<endl;
return 0;
}