这个周末,Joe 一如既往的拿出了外卖的点餐单。菜单上从上到下列着 N 个菜, 每个菜 Joe 会列出一个美味值。Joe 这次希望从菜单上连续地点 K 个菜,并且美 味值之和最大。
但在计算美味值之和时,Joe 有自己的一套计算方法:由于他会按照顺序享受这 K 个菜,并且越吃越后面的菜他就越享受,所以对于他吃的第 i 个菜,设它的美 味值为 D,美味值之和就会加上 D*i。例如,如果他要顺序吃美味值为 3, 8, 5 的 菜,那么美味值之和为 3 + 8*2 + 5*3 = 34。
Joe 给出了这 N 个菜的美味值,他想知道所能达到最大的美味值之和。
t1 dish
这个题用前缀和模拟,1*1,2*2,3*3,4*4,5*5-2*1 ,3*2, 4*3 ,5*4 ,6*5
你会发现少了个1-4的和,多了个5*6
所以维护前缀和就行了
#include<cstdio>
#include<iostream>
using namespace std;
int s[1100000],n,k,a[1199999];
long long ans,tot;
int main(){
freopen("dish.in","r",stdin);
freopen("dish.out","w",stdout);
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),s[i]=s[i-1]+a[i];
for(int i=1;i<=k;i++) tot+=i*a[i];
ans=tot;
for(int i=k+1;i<=n;i++){
tot-=a[i-k];
tot-=s[i-1]-s[i-k];
tot+=k*a[i];
ans=max(tot,ans);
}
printf("%lld",ans);
}
t2 champion
ancelot 市近期要举办大奖赛啦!住在市里的市民都十分兴奋,Morgan 也不例 外。他查了一下比赛的信息,发现比赛一共由 N 场,并且每一场的门票价格可能 会不相等。Morgan 留给比赛的预算是 K 元;他想知道,一共有多少种买票的方 案,使得门票之和不超过 K 呢?
60
背包f[j]表示花j元钱的方案数
#include<cstdio>
int n,k,a[1999999];
long long f[1999999];
int main(){
freopen("champion.in","r",stdin);
freopen("champion.out","w",stdout);
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
f[0]=1;
for(int i=1;i<=n;i++)
for(int j=k;j>=a[i];j--)
f[j]+=f[j-a[i]];
for(int i=1;i<=k;i++)f[i]+=f[i-1];
printf("%lld",f[k]);
}
100
k’th number的变式
用折半搜索,2^40太大
2^20就ok了
先处理处前一半的组合,与后一半的组合;
排序
然后再枚举第一个里的,二分出第一个小于k的,加上前面的
也可以用two point。。。zhw说的。。。。
用两个指针搞
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int n;long long a[45],k,ans,b[2][1<<21];
void dfs(bool opt,int l,int r,long long s){
if(l<=r)
{
b[opt][++b[opt][0]]=s+a[l];
dfs(opt,l+1,r,s+a[l]);
dfs(opt,l+1,r,s);
}
}
int main(){
freopen("champion8.in","r",stdin);
freopen("champion.out","w",stdout);
scanf("%d%lld",&n,&k);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
b[0][0]=b[1][0]=1;
dfs(0,1,n/2,0);
dfs(1,n/2+1,n,0);
sort(b[0]+1,b[0]+b[0][0]+1);
sort(b[1]+1,b[1]+b[1][0]+1);
int r=b[1][0];
for(int i=1;i<=b[0][0];i++){
while(b[1][r]+b[0][i]>k) r--;
//if(i>r) break;
ans+=r;
}
printf("%lld",ans);
}
t3
hidden
Irene 想用以下的方法加密一条信息(这是她从密码学书上自学来的):
假定这条信息可以用一个字符串 S 表示,其中 S=BCAAD.(其中‘.’代表字符串结 尾)。Irene 首先把 S 的所有循环同构串写下来(所谓循环同构即是不断地把字符 串开头的字符移动到尾端):
BCAAD.
CAAD.B
AAD.BC
AD.BCA
D.BCAA
.BCAAD
接下来她会把这些字符串都排序:
.BCAAD
AAD.BC
AD.BCA
BCAAD.
CAAD.B
D.BCAA
接下来她会把这些字符串的最后一位按照顺序写下来得到加密串 T=DCA.BA
现在 Irene 发现在密码学的书上写着一条来历不明的加密串 T。她迫切地知道, 如果信息是按照这种方式加密的,那么原串是什么?
因为是按字典序排的,且字母顺序不会变,所以你只要把他们排个序,就知道第一列的序列了
这样就知道一个字母后面的了,第一个是从最后一个过去的。
然后相同的字母你给他编个号,就不相同了
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
struct st{
char s;
int t;
};
bool cmp(const st &a,const st &b){
return a.s<b.s||a.s==b.s&&a.t<b.t;
}
char q[31000];
st a[31000],b[31000];
int last[31000],x;
int main(){
freopen("hidden.in","r",stdin);
freopen("hidden.out","w",stdout);
scanf("%s",q+1);
for(int i=1;i<=strlen(q+1);i++) a[i].s=q[i],a[i].t=i,b[i].t=i,b[i].s=a[i].s;
sort(a+1,a+1+strlen(q+1),cmp);
for(int i=1;i<=strlen(q+1);i++){
last[b[i].t]=a[i].t;
if(b[i].s=='.') x=b[i].t;
}
//for(int i=1;i<=strlen(a+1);i++) printf("%d",last[b[i]]);
int w=last[x];
while(x!=w){
printf("%c",b[w].s);
w=last[w];
}
printf(".");
}
t4
backets
课堂上,Felix 刚刚学习了关于括号序列的知识。括号序列是一个只由左括号“(” 和右括号“)”构成的序列;进一步的,一个合法的括号序列是指左括号和右括号能 够一一匹配的序列。
如果用规范的语言说明,一个合法的括号序列可以有以下三种形式:
1S=“”(空串),S 是一个合法的括号序列;
2S=XY,其中 X,Y 均为合法的括号序列,则 S 也是一个合法的括号序列;
3S=(X),其中 X 为合法的括号序列,则 S 也是一个合法的括号序列。
这时老师在黑板上写出了一个了括号序列:“()))()”。 Felix 一眼就看出这个序列并不是合法的括号序列。
这时老师提出了一个这样的问题:能否在序列中找出连续的一段,把这一段里面
的左括号变成右括号,右括号变成左括号,变换之后整个序列可以变成合法的呢?
Felix 想到,可以把[3..5]进行调换,这样序列就会变为()(()),是一个合法的序列。 很明显,不止有一种方法可以使整个序列变合法。
这时,老师又在黑板上写出了一个长度为 N 的括号序列。Felix 想,能否对这个 序列进行至多一次变换,使它变合法呢?
这个题暴力50,可以枚举从哪里翻转,n^2
然后在判断是不是合法的序列,n的,所以是n^3*t
100用动规
f[i][j][k]指到第i位,有j个’(‘,k代表翻没翻转
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
char a[9990];
int t,f[2][5001][3];
int main(){
freopen("brackets.in","r",stdin);
freopen("brackets.out","w",stdout);
scanf("%d\n",&t);
while(t--){
scanf("%s",a+1);int n=strlen(a+1);
memset(f[0],0,sizeof f[0]);
f[0][0][0]=1;int pre=0,nex=1;
for(int i=1;i<=n;i++)
{
memset(f[nex],0,sizeof f[nex]);
for(int j=0;i+j-1<=n&&j<=i;j++)
{
if(f[pre][j][0]){//为翻转
if(a[i]=='(') f[nex][j+1][0]=1;
else if(a[i]==')'&&j>0) f[nex][j-1][0]=1;
if(a[i]==')') f[nex][j+1][1]=1;
else if(a[i]=='('&&j>0) f[nex][j-1][1]=1;
}
if(f[pre][j][1]){//在反转
if(a[i]==')') f[nex][j+1][1]=1;
else if(a[i]=='('&&j>0) f[nex][j-1][1]=1;
if(a[i]=='(') f[nex][j+1][2]=1;
else if(a[i]==')'&&j>0) f[nex][j-1][2]=1;
}
if(f[pre][j][2]){//已翻转
if(a[i]=='(') f[nex][j+1][2]=1;
else if(a[i]==')'&&j>0) f[nex][j-1][2]=1;
}
}
swap(nex,pre);
}
if(f[pre][0][0]||f[pre][0][1]||f[pre][0][2]) printf("possible\n");
else printf("impossible\n");
}
}