A
题意:给你n个数,从中选出一些数字,使得数字和的数字根分别为1~9的组合分别有多少种
结论:一个数的数字根等于这个数对9取模的结果(特别地,取模得 0则数字根为9)
问题转化为:从{𝑎𝑛}中选择一些数字使得其求和对9取模得0,1,2,3,4,5,6,7,8有多少种选法;
思路:这是一个经典的0/1背包变形的DP问题;令𝑑𝑝[𝑖][𝑗]表示考虑了前𝑖个数,选择了一些数字使得求和对9取模得𝑗的方案数;
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxx = 1e5 + 10;
const int mod=998244353;
int n;
int dp[maxx][10];
void solve()
{
scanf("%d",&n);
dp[0][0]=1;
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
x%=9;
for(int j=0;j<=8;j++)
{
dp[i][(j+x)%9]=(dp[i][(j+x)%9]+dp[i-1][j])%mod;
dp[i][j]=(dp[i][j]+dp[i-1][j])%mod;
}
}
for(int i=1;i<=9;i++)
{
if(i==9) printf("%d",dp[n][0]-1);
else printf("%d ",dp[n][i]);
}
}
int main()
{
//scanf("%lld", &t);
//while (t--)
//{
solve();
//}
system("pause");
}
C
题意:两条读写语句间隔少于三句会发生读写问题,可以在两条读写语句中插入空语句解决问题,给定原程序,求最少插入空语句的操作次数
思路:开一个数组pos记录原程序语句在新程序中的位置,遍历原程序,用t表示在新程序的第t行,发生冲突则t++增加空语句,遍历完第i句的冲突之后,插入到新程序第t句,更新pos[i]
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxx = 1e6 + 10;
int t, n;
int a[110][5];
int pos[10000];//pos[i]=x表示原程序第i句在新程序中是第x句
void solve()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
t++;//t表示新程序中第t行
for(int j=1;j<=3;j++)
{
scanf("%d",&a[i][j]);
if(a[i][j])
{
while(t-pos[i-j]-1<3)
{
t++;
}
}
}
pos[i]=t;//遍历完第i句的冲突之后,插入到新程序第t句
}
printf("%d",t-n);
}
int main()
{
//scanf("%lld", &t);
//while (t--)
//{
solve();
//}
system("pause");
}
E
题意:n个人进学校,m个人带了校园卡,先让m个人带校园卡进入学校,再派一个人带着所有m张校园卡出来,重复上述过程,直到所有人进入学校。进出都需要花费一个单位时间,问共花费多少时间
思路:首先n>1&&m==1时无法全部进入,若n<=m,则只需花费1分钟,当n>m时,只能分多批进入。一批相当于只进入了m-1个人,进行i轮进m出1后,还剩下n-i*(m-1)人,当剩下的人数小于等于m时,即i>=(m-n)/(1-m),(注意,i为轮数需要向上取整) 最后一批(花费1时间)可以全部进入。共花费2*i+1时间.
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxx = 1e6 + 10;
ll t, n, m;
void solve()
{
scanf("%lld %lld", &n, &m);
if (n > 1 && m == 1)
printf("-1\n");
else
{
if (n <= m)
printf("1\n");
else if (n > m)
{
ll i=(double)(1.0*m-n)/(1.0-m);
if((m-n)%(1-m)!=0) i++;//向上取整
ll ans=i*2+1;
printf("%lld\n",ans);
}
}
}
int main()
{
scanf("%lld", &t);
while (t--)
{
solve();
}
system("pause");
}
F
题意:给定一个长为n的数组a和一个整数m,你需要将其切成连续的若干段,使得每一段的中位数都大于等于m,求最多可以划分成多少段。
解法:记数列中≥ 𝒎的数字有𝒄𝒏𝒕𝟏个,< 𝒎的数字有𝒄𝒏𝒕𝟐个,则答案为 𝒄𝒏𝒕𝟏 − 𝒄𝒏𝒕𝟐,该值≤ 𝟎时输出-1
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxx = 1e5 + 10;
int n,m,t;
void solve()
{
scanf("%d %d",&n,&m);
int cnt1=0,cnt2=0;
int tem;
for(int i=1;i<=n;i++)
{
scanf("%d",&tem);
if(tem>=m) cnt1++;
else cnt2++;
}
if(cnt1-cnt2<=0) printf("-1\n");
else printf("%d\n",cnt1-cnt2);
}
int main()
{
scanf("%d", &t);
while (t--)
{
solve();
}
system("pause");
}
J
题意:A个安静的小朋友和B个闹腾的小朋友,选n个小朋友围成圈做游戏,但闹腾小朋友不能相邻,给定每个小朋友的幸福度,求出最大幸福度
思路:先将两组幸福度进行降序排序分别叫做arr和brr数组。如果选定安静小朋友人数<选定的闹腾小朋友的人数则不能进行游戏,否则最优方案一定是从arr和brr中各取一个前缀和。因此可以求出两个数组的前缀和,然后枚举从A组中选了多少人(从 B中选的人数等于总人数n减去A中的),利用前缀和𝑂(1)的获得此时的总幸福度。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxx = 1e4 + 10;
ll t,n,A,B;
ll arr[maxx],brr[maxx];
ll sa[maxx],sb[maxx];
bool cmp(ll a,ll b)
{
return a>b;
}
void solve()
{
ll ret=0,ans=-0x3f3f3f3f;
scanf("%lld %lld %lld",&A,&B,&n);
for(ll i=1;i<=A;i++) scanf("%lld",&arr[i]);
for(ll i=1;i<=B;i++) scanf("%lld",&brr[i]);
sort(arr+1,arr+A+1,cmp);
sort(brr+1,brr+B+1,cmp);
for(ll i=1;i<=A;i++) sa[i]=sa[i-1]+arr[i];
for(ll i=1;i<=B;i++) sb[i]=sb[i-1]+brr[i];
ll tem=0;
if(n%2==0) tem=n/2;//i>=n-i控制i的下界
else tem=n/2+1;//n为奇数时会向下取整,所以要+1
for(int i=min(A,n);i>=tem;i--)//注意i从A和n的最小值开始枚举
{
if(n-i>B||n-i>i) continue;//选中B组人数n-i大于B组总人数||闹腾人数>安静人数
ret=sa[i]+sb[n-i];
ans=max(ans,ret);
}
if(ans<0) printf("-1\n");
else printf("%lld\n",ans);
}
int main()
{
scanf("%lld",&t);
while (t--)
{
solve();
}
system("pause");
}
H
题意:
注意到n<=1e6,0<=ai<=1000的,所以会有大量的值重复出现
观察发现,答案与给定序列ai的顺序无关,因为是两层for循环。
思路:开一个cnt数组记录元素出现的次数,然后枚举所有的<i,j>对儿
i!=j时,对答案的贡献为cnt[i]*cnt[j]*abs(i+j-1000)
注意i=j时,对答案的贡献为(cnt[i]+)*abs(i+j-1000)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxx = 1e6 + 10;
int n;
ll ans;
ll a[maxx];
ll cnt[1010];//cnt[i]为元素i出现的次数
int main()
{
ll add=0;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]),cnt[a[i]]++;
for(ll i=0;i<=1000;i++)
{
for(ll j=i;j<=1000;j++) //枚举i,j对儿,j从i开始枚举
{
if(i==j) add=(cnt[i]+cnt[i]*(cnt[i]-1ll)/2ll);//自己跟自己配对+与别人配对
else add=(ll)cnt[i]*cnt[j];
ans+=add*(ll)abs(i+j-1000);
}
}
printf("%lld",ans);
system("pause");
}