A.跑步训练
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N=2e5+5;
int n=10000;
int res;
int main(){
while(n>=600){
n-=300;
res+=2*60;
}
res+=n*60/600;
cout<<res;
}
B.合并检测(均值不等式 / 打表)
① 均值不等式
② 打表
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N=2e5+5;
int n=10000;
int main(){
for(int k=1;k<=100;k++){
int g=(n+k/2)/k;//总组数
int p=min(g,int(n*0.01));//被感染组数(均匀分布)
cout<<k<<" "<<p*(k+1)+(g-p)<<endl;
}
}
C.分配口罩
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N=2e5+5;
int w[]={9090400, 8499400, 5926800, 8547000, 4958200, 4422600, 5751200,
4175600, 6309600, 5865200, 6604400, 4635000, 10663400, 8087200, 4554000};
int check(int x){
int a=0,b=0;
for(int i=0;i<15;i++){
if(x>>i&1) a+=w[i];
else b+=w[i];
}
return abs(a-b);
}
int main(){
int res=0x3f3f3f3f;
for(int i=0;i<1<<15;i++){
res=min(res,check(i));
}
cout<<res;
}
D.矩阵(DP)
表示前i个数顺序放置,且第一行放了j个数的合法方案数
① 保证右边比左边大,放置时需要从小到大顺序放置,即可满足
② 保证下边比上边大,如果将第i个数放到第二行,需要判断放置后第二行数的个数是否<=第一行数个数,如果不满足说明对于该列先放下边后放上边,不满足下边大于上边
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N=2055,mod=2020;
int f[N][N];
//f[i][j] 前i个数 第一行放了j个
//按顺序放,即先放左边再放右边,能保证右边大于左边
//第二行的个数<=第一行 保证先放上面再放下面,即下边比上边大
int main(){
f[0][0]=1;
for(int i=1;i<=2020;i++){
for(int j=0;j<=i;j++){
//第一行
f[i][j]=(f[i][j]+f[i-1][j-1])%mod;
//第二行
if(i-j<=j){
f[i][j]=(f[i][j]+f[i-1][j])%mod;
}
}
}
cout<<f[2020][1010];
}
E.完美平方数
F.解码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N=2055;
string s;
int main(){
cin>>s;
char c;
for(int i=0;i<s.length();i++){
if(isdigit(s[i])){
for(int j=0;j<s[i]-'0'-1;j++){
cout<<c;
}
}else{
cout<<s[i];
c=s[i];
}
}
}
G.走方格(数字三角形DP)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N=2055;
int f[N][N];
int n,m;
int main(){
cin>>n>>m;
f[1][1]=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(i%2==0&&j%2==0) continue;
f[i][j]+=f[i-1][j];
f[i][j]+=f[i][j-1];
}
}
cout<<f[n][m];
}
H.整数拼接(数位技巧)
如果A和B组合的数X是K的倍数,则有X=A*10^z+B(z为B的位数),且A*10^z %K==(-B%K+K)%K,即两数模K的余数互补,那么我们可以考虑将模K相等的数的个数记录下来。
对于每个数A,枚举其*10的位数,并根据模K的余数记录个数,进行预处理。对于每个数B,其作为低位时,与其匹配的高位数的个数为mp[z][(-B%K+K)%K](z为B的位数)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N=1e5+5;
int n,k;
int w[N];
int mp[11][N];
//mp[i][j] x*10^i %k == j 的x的数量
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d",&w[i]);
int t=w[i]%k;
for(int j=0;j<=10;j++){
mp[j][t]++;
t=t*10%k;
}
}
ll res=0;
for(int i=1;i<=n;i++){
int t=w[i],d=0;
while(t) t/=10,d++;
int nd=(k-w[i]%k)%k;
res+=mp[d][nd];
//判断是否包含w[i]和w[i]
int tw=w[i]%k;
while(d--) tw=tw*10%k;
if(tw==nd) res--;
}
cout<<res;
}
I.超级胶水(规律分析)
最终的结果等于每两个石子之间都相乘的和
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N=1e5+5;
int n;
int w[N];
ll sum[N];
ll res;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&w[i]);
sum[i]=sum[i-1]+w[i];
}
for(int i=1;i<=n;i++){
res+=(ll)w[i]*(sum[n]-sum[i]);
}
printf("%lld",res);
}
J.网络分析(并查集高阶操作)
做法一:每次合并时,如果这两个点不在同一连通块,则构造一个新点,使这个新点成为合并后的根节点,同时从根节点向两个点连边。最后从新点中的根节点fa开始DFS,将fa的权值传递给子节点
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N=2e4+5,M=N<<2;
int n,m;
int p[N];
int h[N],e[M],ne[M],idx;
void add(int a,int b){
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
int f[N];
int new_node;
int find(int x){
if(p[x]!=x) p[x]=find(p[x]);
return p[x];
}
void dfs(int u,int fa){
f[u]+=f[fa];
for(int i=h[u];~i;i=ne[i]){
int j=e[i];
dfs(j,u);
}
}
int main(){
cin>>n>>m;
new_node=n+1;
memset(h,-1,sizeof h);
for(int i=1;i<=2*n;i++) p[i]=i;
while(m--){
int c,a,b;cin>>c>>a>>b;
if(c==1){
a=find(a),b=find(b);
if(a!=b){
p[a]=p[b]=new_node;
add(new_node,a);
add(new_node,b);
new_node++;
}
}else{
a=find(a);
f[a]+=b;
}
}
for(int i=n+1;i<new_node;i++){
if(p[i]==i) dfs(i,0);
}
for(int i=1;i<=n;i++){
cout<<f[i]<<" ";
}
}
做法二:维护到根节点的权值,一个点的权值等于从该点到根节点的路径上的所有点的权值之和。合并两个根节点时,例如将a合并到b上,为了保证并查集中的点之前的权值不受影响,需要将a的权值减去b的权值抵消,再将a的父节点更新为b. 最后查询时,对于根节点则直接输出权值,非根节点的权值等于该点权值加上根节点的权值
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N=1e4+5,M=N<<1;
int n,m;
int p[N];
int d[N];
int h[N],e[M],ne[M],idx;
void add(int a,int b){
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
int find(int x){//x是根节点 或 x的父节点是根节点 则不压缩
if(p[x]!=x&&p[p[x]]!=p[x]){
int fa=find(p[x]);
d[x]+=d[p[x]];
p[x]=fa;
}
return p[x];
}
int main(){
cin>>n>>m;
memset(h,-1,sizeof h);
for(int i=1;i<=n;i++) p[i]=i;
while(m--){
int c,a,b;cin>>c>>a>>b;
if(c==1){
a=find(a),b=find(b);
if(a!=b){
d[a]-=d[b];
p[a]=b;
}
}else{
a=find(a);
d[a]+=b;
}
}
for(int i=1;i<=n;i++){
if(find(i)==i) cout<<d[i]<<" ";
else cout<<d[i]+d[find(i)]<<" ";
}
}