A 筛法预处理出素数表后进行遍历m的最大值,复杂度O(nloglogn),也可以遍历的时候判断素数复杂度O(nsqrt(n))
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define maxx 500000
int all[maxx];
int ans,n,k;
signed main()
{
cin>>n;
all[1]=1;
for(int i=2;i<=maxx;++i)
{
if(all[i]==1)continue;
for(int j=2*i;j<=maxx;j=j+i)
 all[j]=1;
}
for(int i=2;i<=maxx;++i)
{
if(all[i]==0&&all[i+n]==1)
{cout<<i;
break;}
}
}
B 作为D的弱化,仅仅在最大值和次大值上使用优惠最优,考虑一种情况,其在这两个值外的其余值使用A优惠,那么交换A优惠和最大值/次大值,使得结果更好,B优惠也类似,因为优惠是非减的,随着价格的升高,优惠不会降低,故而在高价格处使用优惠不会使结果更糟。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define maxx 3000000
int all[maxx];
double ans;
int a,b,x,y,n,t;
int cmp(int a,int b)
{
 return a>b;
}

 

signed main()
{
cin>>t;
for(int i=1;i<=t;++i)
{
 cin>>n>>x>>y;
    a=1;
    b=1;
 for(int i=1;i<=n;++i)
  cin>>all[i];
 sort(all+1,all+1+n,cmp);
 ans=0;

  if(n==1)
  {
     double z=all[1]*x/100.0;
     double q=(all[1]-min(all[1],y))*1.0;
    ans=min(z,q);
  }
  else
  {
    double z=all[1]*x/100.0;
     double q=(all[1]-min(all[1],y))*1.0;
       double z1=all[2]*x/100.0;
      double q1=(all[2]-min(all[2],y))*1.0;
      ans=min(z+q1,q+z1);
       for(int i=3;i<=n;++i)
        ans=ans+all[i];
  }
   printf("%lf\n",ans);
}
}
C
与前几个题解不同,不使用容斥,取而代之将txt序列个数作为一维进行转移。
考虑dp,定义dp[i][j][k][x],
i:序列长度
j:当前位置元素,其中0代表t,1代表x,2代表其他
k:当前位置前一个的元素
x:是否存在txt序列
有转移方程
dp[i][j][k][x][w]+=dp[i-1][k][x][w] (如果xkj不构成txt)
dp[i][j][k][x][w]+=sum*(dp[i-1][k][x][0]+dp[i-1][k][x][1]) (如果xkj构成txt,如果j为2则sum为24,否则为1)
由于2代表其他字母,有24个,故而转移的时候要使用sum。
统计∑dp[n][j][k][1]
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define maxx 300000
#define mod 998244353
#define INF 0x3f3f3f3f
int dp[maxx][3][3][2]; //t,x,其他
int all[maxx];
int ans;
int a,b,x,y,n,t,l;

signed main()
{
cin>>t;
for(int i=1;i<=t;++i)
{
 cin>>all[i];
 n=max(n,all[i]);
}
 for(int i=0;i<=n;++i)
  for(int j=0;j<=2;++j)
   for(int k=0;k<=2;++k)
    dp[i][j][k][0]=dp[i][j][k][1]=0;
    
    int noww;
     for(int j=0;j<=2;++j)
      for(int k=0;k<=2;++k)
      {noww=1;
       if(j==2) noww=noww*24;
       if(k==2) noww=noww*24;
       dp[2][j][k][0]=noww;
       }
      for(int i=3;i<=n;++i)
        for(int j=0;j<=2;++j)
         for(int k=0;k<=2;++k) 
          for(int x=0;x<=2;++x)
           {if(j==2) noww=24;
           else noww=1;
           if(x==0&&k==1&&j==0)
           dp[i][j][k][1]=(dp[i][j][k][1]+dp[i-1][k][x][0]+dp[i-1][k][x][1])%mod;
           else
           {dp[i][j][k][0]=(dp[i][j][k][0]+(dp[i-1][k][x][0]*noww)%mod)%mod;
           dp[i][j][k][1]=(dp[i][j][k][1]+(dp[i-1][k][x][1]*noww)%mod)%mod;
           }
           }
     
    for(int i=1;i<=t;++i)
    {
         ans=0;
        for(int j=0;j<=2;++j)
         for(int k=0;k<=2;++k) 
          ans=(ans+dp[all[i]][j][k][1])%mod;
       cout<<ans<<"\n";
   
    }
}



 D 
    与现有题解贪心方法略有不同,并且补充了B使用连续的说明。
    考虑贪心并且枚举所有端点,首先有一个结论,B的使用必然是连续,考虑如下证明,将商品价格自大而小排序,假设存在一个点,此点以后(包括此点),商品价格均小于y,依照这个点将区间分为两个区间,考虑这两个区间,假定左区间分到x个B使用,右区间分到y个B使用,那么左区间的B使用不论放在什么位置对最终答案都没有影响,因为减少的是定值,所以考虑连续放在左区间的右端点附近,因为如果在此区间使用A,那么这样做至少不会导致使用A出现更糟的结果,对于右区间,在右区间的左端点附近连续使用B更优,这样做后考虑剩余位置放若干个A,交换A和任意的B,设交换A和B的位置值为a和b,则需要比较pa+b和pb+a的值,这个值是单调的,故而不进行交换结果更好,结合以上两种说明,至少在临界点B的使用是连续的,推广关于临界点的论断,剩余两个均有单调性,B在这两段分别连续,结合临界点,可以得知B的使用连续。
已知B的使用连续后,考虑对B最开始使用的左端点进行枚举,而右端点的可行位置是一个与n无关的常数,能够产生可行位置的区间长度分别为:A操作最终值开始大于B操作值的临界点,0,B操作使用最大次数,n-B操作使用次数,商品价格开始小于y的临界点。
    能够枚举这些右端点位置而不忽略最优解的原因是:考虑由两个临界点划分产生的三个区间,如果在这三个区间内枚举,答案是单调的,因为依照这样的划分方式,能够使得此区间内的答案不受选择A还是选择B最优,选择B会不会出现负数这两个条件的干扰,而除去这两个临界点,剩余的枚举是防止特殊情况造成的影响,包括:A,B其中有一个为0,在区间内,此时可供使用B次数过少,无法达到区间端点;其中n-B操作次数是在A+B大于n时合法枚举的保证。
    基于上述的枚举模式不适用与A+B<N,因为此时不必要枚举B的使用次数,因为A与B过少,在前A+B内自然全部使用,故而要进行特殊处理。
    其余细节参见代码。
    
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define maxx 300000
#define INF 0x3f3f3f3f
int all[maxx];
int all1[maxx];
double ans;
int a,b,x,y,n,t,l;
int fg1,fg2;
int cmp(int a,int b)
{
 return a>b;
}
int cx(int i,int j) //查询[i,j]区间内的数字和
{
  if(i>j) return 0;
  return all1[j]-all1[i-1]; 
}
int pd(int i,int l) //判断长度是否合法
{
   if(i+l-1<=n&&l>=n-a&&l<=b) return 1;
   else return 0;
}
void solve(int i,int l) //枚举区间起点和长度后进行计算
{
   if(l<0) return ; //不合法
   int aa=cx(i,i+l-1); //使用B区间长度
   double ans1=0;
    //判断是否出现小于y临界点
   if(fg2>=i&&fg2<=i+l-1) aa=aa-(fg2-i+1)*y-cx(fg2+1,i+l-1);
   else if(fg2<i) aa=0;
   else aa=aa-l*y;
    //剩余部分用A
   if(a+b>=n)
   ans1=(cx(1,n)-cx(i,i+l-1))*x/100.0+aa;
    //对于A+B<N
   else
   {ans1=(cx(1,a+b)-cx(i,i+l-1))*x/100.0+aa;
   ans1=ans1+cx(a+b+1,n);
   }
   ans=min(ans,ans1);
} 


signed main()
{
cin>>t;
for(int i=1;i<=t;++i)
{
 cin>>n>>a>>b>>x>>y;
 for(int i=1;i<=n;++i)
  cin>>all[i];
 sort(all+1,all+1+n,cmp);
  for(int i=1;i<=n;++i)
  all1[i]=all1[i-1]+all[i];
   fg1=n+1;
   fg2=n+1;
    //临界点1
   for(int i=1;i<=n;++i)
   {
    if(all[i]*x>(all[i]-y)*100)
    {
      fg1=i-1;
      break;
    }
   }
    //临界点2
    for(int i=1;i<=n;++i)
   {
    if(all[i]<y)
    {
      fg2=i-1;
      break;
    }
   }


   ans=INF*1.0;

   
  for(int i=1;i<=n;++i)
  {
    if(a+b<n) 
    {
      solve(i,b);
      if(i>a) break; 
      continue;
    }
      if(pd(i,0))
      solve(i,0);
      if(pd(i,b))
      solve(i,b);
      if(pd(i,n-a))
      solve(i,n-a);
      if(pd(i,fg1-i+1))
      solve(i,fg1-i+1);
      if(pd(i,fg2-i+1))
      solve(i,fg2-i+1);
      if(pd(i,n-i+1))
      solve(i,n-i+1);

   
    }
  
  printf("%lf\n",ans);
  }

}

E
变形背包
与前一个题解略有区别,未使用复杂数据结构,如线段树等。
根据题意的叙述,引入了最多花费金钱,容易想到背包问题,但在这道题目,简单的背包设计无法避免后效性,如当处理到第i个元素,由于每次消除的总元素是偶数,所以到第i个元素后,如果选择消除,无法判断1.是否是合法的消除 2.和谁消除。
针对1,修改转移方式,每次每次利用它自身和后一个元素进行转移,如此转移一定是偶数。
针对2,依照1的转移方式后,容易使人联想,一定是当前元素和其后一个元素被消除吗?事实上不一定是这样,这里涉及到两个消除段的合并,如果转移到的状态的末尾段的末尾元素是当前元素前一个元素,那么这两个消除段可以进行合并,并且这样的合并一定是合法的,
因为完全可以构造一种消除方式,每次消除前一段的开头和后一段末尾,否则不可以,同时合并两个消除段,增加的价值仅仅是前一个元素和前两个元素之间的价值,这就说明了不必末尾记录消除段长。
为了判断末尾段元素是否是当前元素的前一个元素,状态内引入一维,用于判别当前位置是否被消除。
 dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j][1]); //未选择,直接转移
//如果j足够大
 if(j>=all[all2[i]]+all[all2[i-1]])
//如果不可以合并,就直接转移,否则合并两段
 dp[i][j][1]=max(dp[i-2][j-all[all2[i]]-all[all2[i-1]]][0]+cx(all2[i-1],all2[i]),dp[i-2][j-all[all2[i]]-all[all2[i-1]]][1]+cx(all2[i-2],all2[i]));
  


另外有一些细节,在消除段的时候,由于被消除的是2,只用遍历2的数组;预处理出消除位置除了2的前缀和,方便查询消除段的价值和;价值和最大为5000*10^9,如果要标记未定义,至少标记到1e13;注意初始状态的设置。
复杂度O(nm)
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define maxx 300
#define mod 998244353
#define INF 0x3f3f3f3f
int dp[maxx][maxx][2]; //
int all[maxx];
int all2[maxx];
int qz[maxx];
int ans;
int a,b,x,y,n,t,l,k,neww1,neww2;

int cx(int i,int j) //查询段长
{
  return qz[j]-qz[i-1];
}

signed main()
{
cin>>t;
for(int i=1;i<=t;++i)
{
 cin>>n>>k;
 neww1=neww2=0;
 for(int i=1;i<=n;++i)
 {
  cin>>x>>all[i];
  qz[i]=qz[i-1];
  if(x==2) all2[++neww2]=i;
  else qz[i]=qz[i]+all[i];
 }
  for(int j=0;j<=k;++j)
  {dp[2][j][0]=dp[2][j][1]=-INF;
   dp[1][j][0]=dp[1][j][1]=-INF;
  }
  dp[1][0][0]=0;
  dp[2][0][0]=0;
  dp[2][all[all2[1]]+all[all2[2]]][1]=cx(all2[1],all2[2]);


  for(int i=3;i<=neww2;++i)
   for(int j=0;j<=k;++j)
  {
    dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j][1]);
    if(j>=all[all2[i]]+all[all2[i-1]])
    dp[i][j][1]=max(dp[i-2][j-all[all2[i]]-all[all2[i-1]]][0]+cx(all2[i-1],all2[i]),dp[i-2][j-all[all2[i]]-all[all2[i-1]]][1]+cx(all2[i-2],all2[i]));
  }
  ans=0;
  for(int j=0;j<=k;++j)
   for(int w=0;w<=1;++w)
  ans=max(ans,dp[neww2][j][w]);
 cout<<ans<<"\n";

  
}

}

F
题解写的挺清楚的,除了题解外也没想到什么更简便的构造方法。