济南CSP-J刷题营集训内容
Day1(基础算法)
T1
求和可以用前缀和。
求平均值时,特判是否整除而输出结果。
求方差,我们直接用他给的公式以分数形式算出结果,维护两个分子和分母,通分相减后特判输出。
注意要输出最简分数,所以我们用
#include<bits/stdc++.h>
#define gt getchar
#define pt putchar
#define int long long
using namespace std;
int a[100005];
int sum1[100010],sum2[100010];
int fc1,fc2;
int gcd(int a,int b){
return(b==0?a:gcd(b,a%b));
}
int lcm(int a,int b){
return a*b/gcd(a,b);
}
signed main(){
int n;
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
sum1[i]=a[i]+sum1[i-1];
cout<<sum1[i]<<' ';
sum2[i]=(a[i]*a[i])+sum2[i-1];
if(sum1[i]%i==0){
cout<<sum1[i]/i<<' ';
}
else{
int g=gcd(sum1[i],i);
cout<<sum1[i]/g<<'/'<<i/g<<' ';
}
int fm1=i;
int fz1=sum1[i];
int g=gcd(fm1,fz1);
fm1/=g;
fz1/=g;
fz1=fz1*fz1;
fm1=fm1*fm1;
int fm2=i;
int fz2=sum2[i];
int l=lcm(fm2,fm1);
int x=l/fm2,y=l/fm1;
fm2*=x;
fz2*=x;
fm1*=y;
fz1*=y;
int fzans=abs(fz2-fz1);
if(fzans%fm1==0)cout<<fzans/fm1<<'\n';
else{
int k=gcd(fzans,fm1);
cout<<fzans/k<<'/'<<fm1/k<<'\n';
}
}
return 0;
}
T2
暴力
直接枚举每一个二元组,然后算出
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=1e5+10;
int a[MAXN],b[MAXN],c[MAXN];
vector<int>x;
signed main(){
int n,k;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)cin>>b[i];
for(int i=1;i<=n;i++)cin>>c[i];
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
x.push_back(a[j]+b[j]*c[i]);
}
}
sort(x.begin(),x.end());
cin>>k;
cout<<x[k-1];
return 0;
}
正解:二分
我们把序列
#include<bits/stdc++.h>
#define int long long
using namespace std;
int a[100010],b[100010],c[100010];
int n;
int check(int x){
int sum=0;
for(int i=1;i<=n;i++)
if(x-a[i]>=0)sum+=upper_bound(c+1,c+n+1,(x-a[i])/b[i])-c-1;
return sum;
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++)cin>>b[i];
for(int i=1;i<=n;i++)cin>>c[i];
sort(c+1,c+n+1);
int l=0,r=1e18+1e9;
int k;
cin>>k;
while(l<r){
int m=(l+r)>>1;
if(check(m)>=k)r=m;
else l=m+1;
}
cout<<r;
return 0;
}
T3
暴力
枚举每一个
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5;
struct pair3{
int f,s,t;
};
vector<pair3>x;
signed main(){
int n,n3,p;
cin>>n>>n3>>p;
const int n0=n*n+1;
for(int i=1;i<=p;i++){
for(int j=0;j<=p;j++){
for(int k=1;k<=p;k++){
int n1,n2;
n1=n0%i;
n2=n1+j;
if(n2%k==n3){
pair3 y={i,j,k};
x.push_back(y);
}
}
}
}
int q=x.size();
if(q>100000){
cout<<q<<'\n';
for(int i=0;i<100000;i++){
cout<<x[i].f<<' '<<x[i].s<<' '<<x[i].t<<'\n';
}
}
else{
cout<<q<<'\n';
for(int i=0;i<q;i++){
cout<<x[i].f<<' '<<x[i].s<<' '<<x[i].t<<'\n';
}
}
return 0;
}
正解:
还能用二分你敢信
T4
不会QAQ
最后只得了
Day2(搜索)
T1
直接枚举全排列,每次枚举所有数,如果相邻每个数的差都不等于
#include<bits/stdc++.h>
#define int long long
using namespace std;
int a[20];
signed main(){
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++)a[i]=i;
int cnt=0;
do{
bool f=false;
for(int i=2;i<=n;i++)
if(abs(a[i]-a[i-1])==k)f=true;
if(!f)cnt++;
}while(next_permutation(a+1,a+n+1));
cout<<cnt;
return 0;
}
T2
给
#include<bits/stdc++.h>
#define int long long
using namespace std;
struct xiangliang{
int x,y;
xiangliang(){
x=0;
y=0;
}
}a[105];
int p[105];
set<int>ans;
signed main(){
int n;
cin>>n;
for(int i=1;i<=2*n;i++)
cin>>a[i].x>>a[i].y;
for(int i=1;i<=2*n;i++)p[i]=i;
int cnt=0;
do{
int x=1;
xiangliang y;
y.x=1;
y.y=1;
for(int i=1;i<=2*n;i++){
if(i&1){
y.x=a[p[i]].x*x;
y.y=a[p[i]].y*x;
}
else
x=y.x*a[p[i]].x+y.y*a[p[i]].y;
}
ans.insert(x);
}while(next_permutation(p+1,p+2*n+1));
cnt=ans.size();
cout<<cnt;
return 0;
}
正解
将枚举全排列改成爆搜,剩下的和
#include<bits/stdc++.h>
#define int long long
using namespace std;
bool vis[1005];
int a[1005],b[1005];
int n;
set<int>ans;
void dfs(int x,int y){
while(x<=2*n&&vis[x])x++;
if(x==2*n+1){
ans.insert(y);
return;
}
vis[x]=true;
for(int i=x+1;i<=2*n;i++){
if(!vis[i]){
vis[i]=true;
dfs(x,y*(a[x]*a[i]+b[x]*b[i]));
vis[i]=false;
}
}
vis[x]=false;
}
signed main(){
cin>>n;
for(int i=1;i<=2*n;i++)cin>>a[i]>>b[i];
dfs(1,1);
cout<<ans.size();
return 0;
T3
这道题属于博弈论问题,我们用
赛时估分
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,a,b;
int search(int a,int b,char p){
if(a>n||b>n){
return a-b;
}
if(p=='A'){
int x=0;
int y=search(a+b,b,'B');
if(a!=0&&b!=0)x=search(a*b,b,'B');
return max(x,y);
}
if(p=='B'){
int x=0;
int y=search(a,a+b,'A');
if(b!=0&&a!=0)x=search(a,a*b,'A');
return min(x,y);
}
}
signed main(){
cin>>n>>a>>b;
cout<<search(a,b,'A');
return 0;
}
T4
赛时骗分失败,爆〇了。
正解:打表
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int a[10][10]={{1},
{1, 1},
{1, 2, 1},
{1, 6, 6, 1},
{1, 24, 90, 24, 1},
{1, 120, 2040, 2040, 120, 1},
{1, 720, 67950, 297200, 67950, 720, 1},
{1, 5040, 3110940, 68938800, 68938800, 3110940, 5040, 1},
{1, 40320, 187530840, 24046189440ll, 116963796250ll, 24046189440ll, 187530840, 40320, 1},
{1, 362880, 14398171200ll, 12025780892160ll, 315031400802720ll, 315031400802720ll,
12025780892160ll, 14398171200ll, 362880, 1}};
int n,m,k;
signed main(){
cin>>n>>m>>k;
if(k==0)cout<<1;
else if(n!=m||k>n)cout<<0;
else cout<<a[n][k];
return 0;
}
得分:
Day3(动态规划)
T1
十分简单的一般 DP。
#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[5][100005];
int f[10][100005][10];
signed main(){
int n;
cin>>n;
for(int i=1;i<=2;i++)
for(int j=1;j<=n;j++)
cin>>a[i][j];
f[1][1][2]=a[1][1];
f[2][1][2]=a[2][1];
for(int j=2;j<=n;j++){
for(int i=1;i<=2;i++){
if(i==1){
f[i][j][1]=max(f[i][j-1][2],f[i+1][j-1][2]);
f[i][j][2]=max(f[i][j-1][1],f[i+1][j-1][2])+a[i][j];
}
else{
f[i][j][1]=max(f[i][j-1][2],f[i-1][j-1][2]);
f[i][j][2]=max(f[i][j-1][1],f[i-1][j-1][2])+a[i][j];
}
}
}
int ans=max(max(f[2][n][1],f[2][n][2]),max(f[1][n][1],f[1][n][2]));
cout<<ans;
return 0;
}
T2
最好想的就是用自己的值更新他人的值,注意边算边
#include<bits/stdc++.h>
#define int long long
#define MOD 998244353
using namespace std;
int f[1005][1005];
int ans=0;
signed main(){
int n;
cin>>n;
f[0][0]=1;
for(int i=0;i<=n;i++){
for(int j=0;j<=i;j++){
f[i+1][j]=(f[i][j]%MOD+f[i+1][j]%MOD)%MOD;
f[i][j+1]=(f[i][j]%MOD+f[i][j+1]%MOD)%MOD;
f[i+1][j+1]=(f[i][j]%MOD+f[i+1][j+1]%MOD)%MOD;
}
}
cout<<f[n][n]%MOD;
return 0;
}
T3
gzy本意想让我们写 DP,但这题用记忆化搜索很轻松就过了,原题:滑雪。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int dx[]={0,0,1,-1},dy[]={1,-1,0,0};
int n,a[1005][1005],f[1005][1005];
int dfs(int x,int y){
if(f[x][y])return f[x][y];
f[x][y]=1;
for(int i=0;i<4;i++){
int xx=dx[i]+x;
int yy=dy[i]+y;
if(xx<1||yy<1||xx>n||yy>n)continue;
if(a[x][y]>a[xx][yy]){
dfs(xx,yy);
f[x][y]=max(f[x][y],f[xx][yy]+1);
}
}
return f[x][y];
}
signed main(){
cin>>n;
int ans=-1;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>a[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
ans=max(ans,dfs(i,j));
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)
cout<<f[i][j]<<' ';
putchar('\n');
}
return 0;
}
T4
爆搜
#include<bits/stdc++.h>
using namespace std;
int a[100005];
int maxn=-1;
int n;
void dfs(int now,int x,int y){
if(x==y)maxn=max(maxn,x);
if(now>n)return;
dfs(now+1,x+a[now],y);
dfs(now+1,x,y+a[now]);
dfs(now+1,x,y);
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
dfs(1,0,0);
cout<<maxn;
return 0;
}
正解:
背包
#include<bits/stdc++.h>
#define int long long
using namespace std;
int a[1005],f[5][500005];
signed main(){
int n,m=0;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i],m+=a[i];
int last=0,cur=1;
for(int i=1;i<=m;i++)f[last][i]=-1e9;
for(int i=1;i<=n;i++){
for(int j=0;j<=m;j++)
f[cur][j]=f[last][j];
for(int j=0;j<=m;j++){
if(j+a[i]<=m)f[cur][j+a[i]]=max(f[cur][j+a[i]],f[last][j]+a[i]);
if(j<a[i])f[cur][a[i]-j]=max(f[cur][a[i]-j],f[last][j]-j+a[i]);
else f[cur][j-a[i]]=max(f[cur][j-a[i]],f[last][j]);
}
last^=1;
cur^=1;
}
cout<<f[last][0];
return 0;
}
得分:
Day4(数据结构)
T1
出现次数
我们先把数组反转方便计算,用前缀和的思路,如果
#include<bits/stdc++.h>
#define int long long
using namespace std;
int a[100005],t[100005];
bool use[100005];
signed main(){
int n,q;
cin>>n>>q;
for(int i=1;i<=n;i++)
cin>>a[i];
reverse(a+1,a+n+1);
for(int i=1;i<=n;i++){
if(!use[a[i]])t[i]=t[i-1]+1;
else t[i]=t[i-1];
use[a[i]]=true;
}
while(q--){
int l;
cin>>l;
cout<<t[n-l+1]<<'\n';
}
return 0;
}
T2
组模拟赛
用一个桶记录
#include<bits/stdc++.h>
#define int long long
using namespace std;
int a[100005];
int use[100005];
int ans[100005];
signed main(){
int n,m;
cin>>n>>m;
bool f=true;int cnt=0;
for(int i=1;i<=n;i++){
f=true;
cin>>a[i];
use[a[i]]++;
for(int j=1;j<=m;j++){
if(use[j]==0){
f=false;
break;
}
}
if(f){
cnt++;
ans[cnt]=i;
for(int j=1;j<=m;j++)if(use[j])use[j]--;
}
}
cout<<cnt<<'\n';
for(int i=1;i<=cnt;i++)cout<<ans[i]<<' ';
return 0;
}
T3
完成作业
赛时 爆搜
正解:
按照时间排序处理输入数据,如果⽬前的⽐之前的优,可以替换掉之前的。
⽤优先队列(小根堆)维护这个可反悔贪⼼。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=100005;
pair<int,int>a[MAXN];
signed main(){
int n;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i].first>>a[i].second;
sort(a+1,a+n+1);
priority_queue<int,vector<int>,greater<int> >q;
int now=0,las=0;
int ans=0;
for(int i=1;i<=n;i++){
now+=a[i].first-las;
las=a[i].first;
if(now>0){
now--;
ans+=a[i].second;
q.push(a[i].second);
}
else{
if(q.top()<a[i].second){
ans-=q.top();
ans+=a[i].second;
q.pop();
q.push(a[i].second);
}
}
}
cout<<ans;
return 0;
}
T4
涂刷油漆
(老师的思路)⽤ ST表/单调队列 处理出每种刷油漆的操作对应的⾼度。
那⼀个柱⼦能达到的⾼度就是覆盖它的操作的最⼤值,这同样可以⽤ ST表或单调队列处理。
最少需要⽤⼏次操作可以从左往右贪⼼,如果⽬前没达到最⼤值就找到最右的那个最⼤的操作。
代码:不会qwq。
讲课:
前缀和:
太简单了,不讲
直接用一个数组 sum[i]=sum[i-1]+a[i]
,如果想求出区间 sum[r]-sum[l-1]
。
差分:
线段树用 update 函数更新区间,然后单点查询输出(划掉);
正解:
#include<bits/stdc++.h>
using namespace std;
int n,a[1000005],sum[1000005];
signed main(){
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
int T;
cin>>T;
while(T--){
int l,r,v;
cin>>l>>r>>v;
sum[l]+=v;
sum[r+1]-=v;//计算差值
}
for(int i=1;i<=n;i++)
sum[i]+=sum[i-1];//前缀和维护
for(int i=1;i<=n;i++)
cout<<a[i]+sum[i]<<' ';//差值+原值
return 0;
}
单调栈:
就是一个栈,不过栈内元素保证单调性。即,栈内元素要么从小到大,要么从大到小。
而单调栈维护的就是一个数 前/后 第一个 大于/小于 他的数。
#include<bits/stdc++.h>
using namespace std;
int n,a[3000006],ans[3000006];
stack<int>s;
signed main(){
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=n;i>=1;i--){//倒序枚举
while(!s.empty()&&a[s.top()]<=a[i])s.pop();//比它小的就删去
if(s.empty())ans[i]=0;//没有比它小的就是0
else ans[i]=s.top();//否则就是栈顶元素
s.push(i);//不要忘记把每个数放进栈中
}
for(int i=1;i<=n;i++)//正序输出答案
cout<<a[i]<<'\n';
return 0;
}
高维前缀和与差分:
朴素方法:
还可以算出每一维前缀和,拆分开进行计算。
链表:
用来解决区间覆盖问题,可以
栈&&队列:
很简单,用指针维护即可。
分别用于 dfs 和 bfs。
PS:递归的原理就是栈。
PS:deque(STL双向队列)的空间很大,见NOI2022 D1T1。
分类:
栈:
- 单调栈
- 笛卡尔树
- 离线 RMQ
队列:
- 队列和双端队列
- 单调队列
- 优先队列(也就是堆,一般为二叉堆)。
单调队列还可以优化DP。
倍增:
倍增算法
给你
定义一个二维数组
但这些数太多了,于是中间“砍一刀 ”,得出两边都是
开
即:
做法:一个区间长为
ST表:
用倍增的思路解决 rmq 问题,预处理时间复杂度为
#include<bits/stdc++.h>
using namespace std;
int f[(int)1e5+5][20];
int a[(int)1e6+5];
vector<int>x(1e6+10);
//x[i]表示长度为i的区间 用两个长度为2^x[i]的区间能够覆盖
int main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];
x[1]=0;
for(int i=2;i<=n;i++)
x[i]=x[i>>1]+1;
// i=111时,x[111]=x[55]+1;1111
for(int i=1;i<=n;i++)
f[i][0]=a[i];
for(int j=1;(1<<j)<=n;j++)
for(int i=1;i+(1<<j)-1<=n;i++)
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
for(int i=1;i<=m;i++){
int l,r;
cin>>l>>r;
int len=r-l+1;
int j=x[len];
cout<<max(f[l][j],f[r-(1<<j)+1][j])<<'\n';
}
return 0;
}
可以用来实现 LCA。
并查集:
有
添加从点
如果点
int go(int p){//求点 p 沿着并查集箭头最后会递归走到哪里,也就是查找 p 所在集合的末端元素
if(to[p]==p)return p;//如果点 p 的下一个点为自己,说明末端元素此时为点 p,也就是指向了自己
else return go(to[p]);//更新点 p 为点 p 的下一个点,继续递归调用沿着箭头寻找。
}
Day5(图论)
T1
直接递归求解。
我们不停地让x,y中较大的那个往上走一步。
这样它们一定会在它们的lca处相遇
统计行走过程中的步数即可。
代码:太简单不写了。
T2
我们考虑一个简单的容斥
我们记录
记录x以及他的父亲以及父亲的父亲形成的序列是
其中
那么对于询问
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=5e5+10;
int nxt[MAXN*2],head[MAXN],to[MAXN*2],cnt;
int dis[MAXN][60],fath[MAXN];
int ans=0;
int k;
void add_edge(int a,int b){
nxt[++cnt]=head[a];
head[a]=cnt;
to[cnt]=b;
}
void dfs(int x){
dis[x][0]=1;
for(int i=head[x];i;i=nxt[i]){
if(to[i]==fath[x])continue;
fath[to[i]]=x;
dfs(to[i]);
for(int j=1;j<=50;j++)
dis[x][j]+=dis[to[i]][j-1];
}
}
int getans(int x,int k){
int ans=dis[x][k];
k--;
int i=x;
for(;i!=1&&k;i=fath[i],k--)
ans=ans+dis[fath[i]][k]-dis[i][k-1];
if(i!=1&&k==0)ans++;
return ans;
}
int pf[100005];
signed main(){
//freopen("input.in","r",stdin);
//freopen("output.out","w",stdout);
int n,m;
cin>>n>>m;
for(int i=1;i<n;i++){
int s,e;
cin>>s>>e;
add_edge(s,e);
add_edge(e,s);
}
dfs(1);
while(m--){
int x,y;
cin>>x>>y;
cout<<getans(x,y)<<'\n';
}
return 0;
}
T3
首先顺序显然是可以传递的(这里需要吐槽ybw的误导)。
即有
但是我们不用管,因为从另一方面,所有条件可以拆成若干
也就是
这跟拓扑序是一样的
为了让字典序尽可能小,那就是要我们求最小字典的拓扑序
我们用一个堆维护所有当前可以加入答案序列的点,每次取出其中最小的并更新能加入答案序列的点,直到所有点都加入答案序列.
PS:直接用 set
存下数据后直接输出能得
正解:
#include <bits/stdc++.h>
#define MAXN 1000005
using namespace std;
int n,ans,cnt,s;
priority_queue<int>q;
int d[MAXN],head[MAXN];
bool in[MAXN];
struct edge{
int to,next;
}e[maxn];
void add(int u,int v){
e[++cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt;
d[v]++;
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
int x,last;
cin>>x>>last;
in[last]=true;
for(int j=1;j<x;j++){
int now;
cin>>now;
in[now]=true;
add(last,now);
last=now;
}
}
for(int i=1;i<=1000000;i++)if((in[i])&&(d[i]==0))q.push(n-i);
while(!q.empty()){
int u=n-q.top();
q.pop();
for(int i=head[u];i;i=e[i].next){
d[e[i].to]--;
if(d[e[i].to]==0)q.push(n-e[i].to);
}
cout<<u<<' ';
}
return 0;
}
T4
那么枚举
我们用
我们每次通过
复杂度在
在这种情况下复杂度大致是
代码
Day5讲课:
树相关:
一般来说,以下定义说明了这一张图是一棵树:
- 直接指明了这是一棵树。
- 是一张
个点, 条边的连通图。 - 一张无环(视重边和自环都是环)的连通图。
- 任意两点之间都有且仅一条简单路径。
- ……
树相关的内容:树的遍历、直径、中心、重心。
树的储存一般用链表,特殊情况下用记录父亲或左儿子右兄弟的方式。
Day6(数学):
我太弱了!
T1
首先,我们预处理需要进位的情况。我们发现只有所有位都是
然后思考解法:
输入一个整数
而如果
不难发现它就是:
原理:一个回文数是根据它前半部分来的,所以它的下一个回文数就是原回文数的最中间的一个(或两个)数加1得来的。
#include<bits/stdc++.h>
using namespace std;
char s[205],ans[205];
signed main(){
cin>>s;
int len=strlen(s);
for(int i=0;i<len&&s[i]=='9';i++){
if(i==len-1){
s[0]='1';
len++;
for(int j=1;j<len;j++)s[j]='0';
}
}
for(int i=0;i<=len-i-1;i++)ans[i]=ans[len-i-1]=s[i];
while(strcmp(ans,s)<=0){
int x=(len+1)/2-1;
while(ans[x--]=='9');
x++;
ans[x]=ans[len-x-1]=ans[x]+1;
for(x++;x<=len-x-1;x++)ans[x]=ans[len-x-1]='0';
}
cout<<ans;
return 0;
}
T2
把所有包含
题目保证答案在 long long 类型,数的倍数有
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n;
const int QQ=2351257898;
vector<int>v,p(20);
signed main(){
p[0]=1;
for(int i=1;i<15;i++)
p[i]=p[i-1]*10;
cin>>n;
for(int i=1;i<=n/QQ;i++)
v.push_back(QQ*i);
for(int i=1;i<=5;i++){
for(int j=0;j<=9999;j++){
int tmp=QQ*p[i-1]+j%p[i-1]+j/p[i-1]*p[i+9];
if(tmp<=n)v.push_back(tmp);
}
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
int ans=0;
for(auto i:v)
ans+=i;
cout<<ans;
return 0;
}
T3
看样例解释,我们发现了只会在
首先我们计算
多个
#include<bits/stdc++.h>
#define MAXN 100010
#define int long long
using namespace std;
int a[MAXN],cnt[MAXN],n,l,r;
int ans[MAXN],tag[MAXN];
int pos;
signed main(){
cin>>n>>l>>r;
for(int i=1;i<=n;i++)
cin>>a[i];
for(int i=1;i<=n;i++)
ans[0]+=l/a[i];
for(int i=1;i<=n;i++)
if(a[i]<MAXN)cnt[a[i]]++;
int s;
for(int i=1;i<=MAXN-1;i++){
s=(l/i+1)*i;
while(s<=r){
tag[s-l]+=cnt[i];
s+=i;
}
}
for(int i=1;i<=n;i++){
if(a[i]<MAXN)continue;
s=(l/a[i]+1)*i;
while(s<=r){
tag[s-l]++;
s+=a[i];
}
}
for(int i=1;i<=r-l;i++)ans[i]=ans[i-1]+tag[i];
for(int i=0;i<=r-l;i++)cout<<ans[i]<<' ';
return 0;
}
T4
01 分数规划,没学。
Day6讲课
与、或、异或
C++中对应的运算符分别是 &
|
^
二进制下:
通俗亿点:
& and与运算 二进制下相同位数的两个数都是1就得1,否则得0
| or或运算 二进制下相同位数的两个数至少有一个为1就得1,否则得0
^ xor异或运算 二进制下相同位数的两个数有且只有一个为1就得1,否则得0。
例题
//其实我是P党起家~
var n,ans:int64;
begin
read(n);
if (n and 1=0) then begin
inc(n);
ans:=n;
end;
if (((n+1)div 2) and 1=1) then begin
ans:=ans xor 1;
end;
write(ans);
end.
分解质因数:
如果一个数的数位之和为
如果一个数的个数位上为
如果一个数的千位及其以前的数位组成的数减去后三位数,得到的答案的绝对值为
和 :
辗转相除法(欧几里得算法)
分解质因子,min-max
组合数:
性质见数学篇:
组合数递推式:
//C(i,j) 0<=i<=n,0<=j<=i 要求这个范围的组合数
for(int i=0;i<=n;i++){
C[i][0]=C[i][i]=1;
for(int j=1;j<i;j++)
C[i][j]=C[i-1][j-1]+C[i-1][j];
}
Day7(全真模拟)
我太惨了!
T1
朴素的模拟,无脑输出。
#include<bits/stdc++.h>
using namespace std;
signed main(){
int n;
cin>>n;
for(int i=1;i<=10*n+3;i++)putchar('#');
putchar('\n');
for(int i=1;i<=6*n+1;i++){
putchar('#');
if(i<=n){
for(int j=1;j<=10*n+1;j++)putchar('.');
putchar('#');
putchar('\n');
continue;
}
for(int j=1;j<=2*n;j++)putchar('.');
if(i!=1&&i!=6*n+1&&i<4*n+n+1){
putchar('#');
for(int j=1;j<=8*n;j++)putchar('.');
putchar('#');
putchar('\n');
continue;
}
if(i==4*n+n+1){
for(int j=1;j<=6*n+1;j++)
putchar('#');
for(int j=1;j<=2*n;j++)putchar('.');
putchar('#');
putchar('\n');
continue;
}
if(i<=6*n+2){
for(int j=1;j<=8*n+1;j++)putchar('.');
putchar('#');
putchar('\n');
continue;
}
}
for(int i=1;i<=10*n+3;i++)putchar('#');
return 0;
}
T2
首先枚举得到实际需要买多少个苹果,记为
我们将
然后我们枚举买多少组
#include<bits/stdc++.h>
#define int long long
using namespace std;
signed main(){
int n,c1,c3,c5;
cin>>n;
cin>>c1>>c3>>c5;
c3=min(3*c1,c3);
c5=min(5*c1,c5);
int m=0;
while(m+m/5*2+(m%5)/3<n)m++;
int ans=m*c1;
for(int i=0;i<=m;i++){
int j=max(m-i*5,0ll);
ans=min(ans,(int)i*c5+j/3*c3+min(c3,j%3*c1));
}
cout<<ans;
return 0;
}
T3
赛时我直接把文章暴力展开,得了
但发现可以不把文章展开,我们把文章当做仅由单词组成。设文章中第 i 个单词的编号为
询问时,我们可以在
#include<bits/stdc++.h>
#define int long long
using namespace std;
string s[1000030],t;
int id[1000030];
int sum[1000030];
signed main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>s[i];
for(int i=1;i<=26;i++)
s[n+i]='a'+i-1;
cin>>t;
int l=t.size();
int now=0;
int cnt=0;
for(int i=0;i<l;i++){
if('a'<=t[i]&&t[i]<='z')
id[++cnt]=t[i]-'a'+1+n;
else if(t[i]=='[')
now=0;
else if(t[i]==']')
id[++cnt]=now;
else now=now*10+t[i]-'0';
}
for(int i=1;i<=cnt;i++)
sum[i]=sum[i-1]+s[id[i]].size();
string ans;
for(int i=1;i<=m;i++){
int x;
cin>>x;
int p=lower_bound(sum+1,sum+cnt+1,x)-sum;
if(p>cnt)ans+='!';
else ans+=s[id[p]][x-sum[p-1]-1];
}
cout<<ans;
return 0;
}
T4
BFS
今天的课程都是在讲前几年的真题。
习题
几乎是 CSP-J 2019 ~ 2022 的所有题,除了一些过水题()
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)