心得
ABCE四个水题,
D在归神代码的思路指导下过了,觉得D这代码绝了……
F和归神糊了糊,开始想过贪心想过dp想过背包,
最后一人提了对牌数dp,一个提出用枚举6种情况代替背包
就把思路凑出来了,然而没时间写了所以补一下……
应该是期末之前最后一次打cf了,这两天再看看大创看看期末就去考试了……
两周之后见.jpg
D.Recover it!
a数组中有n(n<=2e5)个数,第i个数是ai(2<=ai<=2e5),
令b数组初始等于a数组,之后对于1到n位置的每个数进行如下操作,
①如果ai是质数,那么把质数表里第ai个质数加入b数组内
②如果ai不是质数,那么把ai的最大因子加入b数组内
现在给你长度为2*n且打乱过的b数组,让你输出一种a数组方案,题目保证有解
考虑3 5 6 10这组样例,如果预取3和5的话,就找不到解了,
所以如果取两个质数,可能会浪费两个合数,
而如果降序取一个合数和它的最大因子,最多浪费一个质数,
所以优先降序取合数,降序是因为最大因子的过程不可逆,
10和25都对应5,但是5不知道序列里它应该对应比它大的是谁
取完合数和它的最大因子之后,再成对枚举质数即可
由于剩余的质数如果可组则两两组成一对,不会产生一个质数与多个质数对应的情形
所以这个增序和降序应该是不影响的
只是归神的代码没有加入2e5以上的数,
所以只能把没加进来的质数的优先级降到最低,也就是增序取
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5;
const int maxm=3e6;
bool ok[maxm];
int num[maxm],v;
int ans[maxm];//存最小的质因子
int a[maxn+5],tot;
int p[maxn+5],cnt;
int n,now,nex;
vector<int>yes,no;
void sieve()
{
for(int i=2;i<maxm;++i)
{
if(!ok[i])p[++cnt]=i;
if(cnt==maxn-1)break;
for(int j=1;j<=cnt;++j)
{
ll tmp=i,k=p[j]*tmp;
if(k>=maxm)break;
ok[k]=1;ans[k]=p[j];
if(i%p[j]==0)break;
}
}
}
int main()
{
sieve();
scanf("%d",&n);
for(int i=1;i<=2*n;++i)
{
scanf("%d",&v);
if(v>2e5)continue;
if(!ok[v])yes.push_back(v);
else no.push_back(v);
num[v]++;
}
sort(no.begin(),no.end(),greater<int>());
for(int i=0;i<no.size();++i)//大数必须先判 不同大数对应一个最大因数 所以从大到小
{
now=no[i];nex=now/ans[now];
if(num[now]&&num[nex])
{
a[++tot]=now;
num[now]--;
num[nex]--;
}
}
sort(yes.begin(),yes.end());//不存在的质数必须后判 不然不能确定留到最后
for(int i=0;i<yes.size();++i)
{
now=yes[i];nex=p[now];
if(!num[now])continue;
if(nex>2e5)
{
a[++tot]=now;
num[now]--;
}
else if(num[nex])
{
a[++tot]=now;
num[now]--;
num[nex]--;
}
}
for(int i=1;i<=tot;++i)
printf("%d%c",a[i],i==tot?'\n':' ');
return 0;
}
F.Destroy it!
n(1<=n<=2e5)轮比赛,第i轮比赛给你ki(1<=ki<=2e5)张牌,保证这n轮牌数之和不超过2e5
当轮的牌只能当轮用,每张牌有两个权值c(1<=c<=3)代表cost,d(1<=d<=1e9)代表damage
你可以选择不超过cost总和不超过3的牌集,打出这些牌的总伤害,
特别地,当你打出第10的倍数张牌的时候,第10的倍数的那张牌的伤害乘2
求总伤害的最大值
dp[i][j]表示i轮打出j张牌的最大伤害,
由于只有dp[i]和dp[i-1]存在递推关系,所以第一维滚动数组优化到dp[2]
由于只有10的倍数会起作用,所以第二维只需要开dp[10]即可,
j%10是等价的,不妨认为每打10张牌历史记录清空一次,从合法状态往后递推即可
每轮的牌的选法,只可能是选cost为0,1,2,3,1+1,1+2,1+1+1的七种选法
所以每轮读入牌的时候,维护c=1的top-3大值,c=2和c=3的最大值即可
每一轮七次dp转移,特判10的倍数,注意滚动数组和最大值的初始化
注意,只要状态经过10的倍数就应该考虑第10-th张牌,比如9到2的转移(1+1+1)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll INF=1e15;//必须有(-INF+最大值)<0 不然会给本为-1的地方产生正值
ll dp[2][10],ans;
ll one[3],two,thr,d;
int n,k,c;
int now,last;
int to;
int main()
{
scanf("%d",&n);
now=0;last=1;
memset(dp,-1,sizeof dp);
dp[now][0]=0;
for(int i=1;i<=n;++i)
{
swap(now,last);
for(int j=0;j<=9;++j)
dp[now][j]=-1;
for(int j=0;j<3;++j)
one[j]=-INF;
two=thr=-INF;
scanf("%d",&k);
for(int j=1;j<=k;++j)
{
scanf("%d%I64d",&c,&d);
if(c==2)two=max(two,d);
else if(c==3)thr=max(thr,d);
else if(c==1)
{
if(d>one[0])one[2]=one[1],one[1]=one[0],one[0]=d;
else if(d>one[1])one[2]=one[1],one[1]=d;
else if(d>one[2])one[2]=d;
}
}
//for(int j=0;j<3;++j)
//printf("one[%d]:%I64d\n",j,one[j]);
//printf("two:%I64d thr:%I64d\n",two,thr);
//1 2 3 1+1 1+2 1+1+1
//1
for(int j=0;j<=9;++j)
if(~dp[last][j])dp[now][j]=dp[last][j];
for(int j=0;j<=9;++j)
{
to=(j+1)%10;
if(~dp[last][j])
{
if(j+1>=10)dp[now][to]=max(dp[now][to],dp[last][j]+2*one[0]);
else dp[now][to]=max(dp[now][to],dp[last][j]+one[0]);
}
}
//2
for(int j=0;j<=9;++j)
{
to=(j+1)%10;
if(~dp[last][j])
{
if(j+1>=10)dp[now][to]=max(dp[now][to],dp[last][j]+2*two);
else dp[now][to]=max(dp[now][to],dp[last][j]+two);
}
}
//3
for(int j=0;j<=9;++j)
{
to=(j+1)%10;
if(~dp[last][j])
{
if(j+1>=10)dp[now][to]=max(dp[now][to],dp[last][j]+2*thr);
else dp[now][to]=max(dp[now][to],dp[last][j]+thr);
}
}
//第10-th牌,不该是等于0,而应该是经过0
//1+1
for(int j=0;j<=9;++j)
{
to=(j+2)%10;
if(~dp[last][j])
{
if(j+2>=10)dp[now][to]=max(dp[now][to],dp[last][j]+2*one[0]+one[1]);
else dp[now][to]=max(dp[now][to],dp[last][j]+one[0]+one[1]);
}
}
//1+2
for(int j=0;j<=9;++j)
{
to=(j+2)%10;
if(~dp[last][j])
{
if(j+2>=10)dp[now][to]=max(dp[now][to],dp[last][j]+one[0]+two+max(one[0],two));
else dp[now][to]=max(dp[now][to],dp[last][j]+one[0]+two);
}
}
//1+1+1
for(int j=0;j<=9;++j)
{
to=(j+3)%10;
if(~dp[last][j])
{
if(j+3>=10)dp[now][to]=max(dp[now][to],dp[last][j]+2*one[0]+one[1]+one[2]);
else dp[now][to]=max(dp[now][to],dp[last][j]+one[0]+one[1]+one[2]);
}
}
//for(int j=0;j<=9;++j)
//printf("dp[%d]:%I64d\n",j,dp[now][j]);
}
for(int j=0;j<=9;++j)
ans=max(ans,dp[now][j]);
printf("%I64d\n",ans);
return 0;
}