今天是2023年4月6日,今天应该是蓝桥杯考前的最后一次刷题了,明天课是真的恶心,晚上还有课,本想逃课的去机房刷题的,但自己还有一节晚自习没有上,明天还有场小白月赛,月赛应该大不了了,去机房也肯定是去不成的了,那么今天晚上总结总结吧。
首先说一说自己的做2022年题的感想吧,我只想说填空题是真的少,dp题我是真的不会!!!虽然我dp在牛客刷了将近40道题,但自己还是不会...言归正传还是写题解吧。
A:
简单的进制转换,但sb的我做错了,答案为2*9^0+0*9^1+2*9^2+2*9^3=1478
B
这题在纸上列举一下即可,但sb的我又做错了!!!
2022012(0-9):10
20221012:1
20221123:1
20221230:1
20221231:1
一共14种
C:
需要注意n的范围要开long long int,如果枚举天数,数据范围将太大会超时,所以需要枚举优化
设sum为每一整周刷题的数目,用n/sum得到的就是需要刷题的整周数,然后在从星期一开始遍历;最后结果为7*(n/sum)+time;
代码如下:
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<stack>
#include<deque>
#include<vector>
#include<map>
#include<set>
#include <utility>
using namespace std;
typedef long long ll ;
#define pii pair<int,int>
const int inf = 0x3f3f3f3f;
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){
if (ch == '-')
f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = (x<<1) + (x<<3) + (ch^48);
ch = getchar();
}
return x * f;
}
void print(__int128 num) {
if(num) {
print(num/10);
putchar(num%10+'0');
}
}
int main(){
ll a,b,n;
cin>>a>>b>>n;
ll time=0;
ll now=5*a+2*b;
ll num=n/now;
ll sum=0;
n=n%now;
while(sum<n){
time++;
if(time%7==0||time%7==6){
sum=sum+b;
}else{
sum=sum+a;
}
}
printf("%lld\n",num*7+time);
return 0;
}
D:
一道还算简单的思维题:但需要仔细看题,每天早上减枝,到晚上树枝会长高一亿亩
看图知答案(画的不咋地...)
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<stack>
#include<deque>
#include<vector>
#include<map>
#include<set>
#include <utility>
using namespace std;
typedef long long ll ;
#define pii pair<int,int>
const int inf = 0x3f3f3f3f;
inline int read() {
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
void print(__int128 num) {
if (num) {
print(num / 10);
putchar(num % 10 + '0');
}
}
int a[10005];
int main() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
a[i] = max((n - i) * 2, (i - 1) * 2);
}
for (int i = 1; i <= n; i++) {
printf("%d\n", a[i]);
}
return 0;
}
E:
一直搞不懂题目上321咋转换成65的,一直受2进制的影响,(321)10=3*8^2+2^10^1+1*2^0
其实不是这样...321中的3是由第二位来的,2是由第一位来的‘
所以321=(3*10+2)*2+1=65
本题还采用了贪心算法,要使得A-B最小即A和B的每一位都为最小进制,自我感觉..
但是要注意的是,用数组存储A和B的每一位的时候要从后往前存储,否则会导致错位,举个例子,A 为 321,B为25,如果正着存储A的3和B的2是一位,A的2和B的5是一位,就错位了。
代码如下:
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<stack>
#include<deque>
#include<vector>
#include<map>
#include<set>
#include <utility>
using namespace std;
typedef long long ll ;
#define pii pair<int,int>
const int inf = 0x3f3f3f3f;
inline int read() {
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
void print(__int128 num) {
if (num) {
print(num / 10);
putchar(num % 10 + '0');
}
}
ll mod = 1000000007;
ll a[100005];
ll b[100005];
ll c[100005];
int main() {
int n;
scanf("%d", &n);
int ma, mb;
scanf("%d", &ma);
for (int i = ma; i >=1; i--) {//从后往前存储,因为如果a为10 4 0,b为8 2,如果从前往后存储的话,10和8代表同一位,而10却是第3位,8为第2位
scanf("%lld", &a[i]);
}
scanf("%d", &mb);
for (int i = mb; i >=1; i--) {
scanf("%lld", &b[i]);
}
ll ans1 = 0;
ll ans2=0;
int maxn = max(ma, mb);
for (int i = 1; i <= maxn; i++) {
c[i]=max((ll)2,max(a[i],b[i])+1); //贪心,要使a-b最小,即每一位都为这一位的最小进制
}
for(int i=ma;i>=1;i--){//简单模拟
ans1=(ans1*c[i]+a[i])%mod;
}
for(int i=mb;i>=1;i--){
ans2=(ans2*c[i]+b[i])%mod;
}
printf("%lld\n", (ans1-ans2+mod)%mod);//一定要记得+mod在模mod,因为在模的过程中ans1可能小于ans2;
return 0;
}
F:
采用暴力写法即枚举起点和端点,然后用二维前缀和,虽然会超时,但是可以骗分,但需要注意的是二维前缀和s[i][j]的i和j代表的是格子,而且i和j都要从1开始
代码如下:
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<stack>
#include<deque>
#include<vector>
#include<map>
#include<set>
#include <utility>
using namespace std;
typedef long long ll ;
#define pii pair<int,int>
const int inf = 0x3f3f3f3f;
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){
if (ch == '-')
f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = (x<<1) + (x<<3) + (ch^48);
ch = getchar();
}
return x * f;
}
void print(__int128 num) {
if(num) {
print(num/10);
putchar(num%10+'0');
}
}
int n,m,num;
int a[505][505];
int s[505][505];
int main(){
cin>>n>>m>>num;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&a[i][j]);
s[i][j]=s[i-1][j]+s[i][j-1]+a[i][j]-s[i-1][j-1];
}
}
int ans;
ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
for(int k=i;k<=n;k++){//
for(int l=j;l<=m;l++){
if((s[k][l]-s[i-1][l]-s[k][j-1]+s[i-1][j-1])<=num){//不要重复定义,上边定义了k为不超过的值,下面又写了层循环,真的服了我了啊
ans++;
}else{
break;
}
}
}
}
}
printf("%d\n",ans);
return 0;
}
可以将二维前缀和进行优化,即加上双指针:
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<stack>
#include<deque>
#include<vector>
#include<map>
#include<set>
#include <utility>
using namespace std;
typedef long long ll ;
#define pii pair<int,int>
const int inf = 0x3f3f3f3f;
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){
if (ch == '-')
f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = (x<<1) + (x<<3) + (ch^48);
ch = getchar();
}
return x * f;
}
void print(__int128 num) {
if(num) {
print(num/10);
putchar(num%10+'0');
}
}
int a[505][505];
int s[505][505];
int main(){
ll n,m,K;
cin>>n>>m>>K;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&a[i][j]);
s[i][j]=s[i-1][j]+s[i][j-1]+a[i][j]-s[i-1][j-1];
}
}
ll ans=0;
for(int i=1;i<=m;i++){
for(int j=i;j<=m;j++){
for(int p=1,q=1;q<=n;q++){
while(p<=q&&s[q][j]+s[p-1][i-1]-s[p-1][j]-s[q][i-1]>K){
p++;
}
if(p<=q){
ans=ans+q-p+1;
}
}
}
}
printf("%lld\n",ans);
return 0;
}
除此之外还可以一维前缀和+双指针:
s[i][j]代表第j列前i行的和
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<stack>
#include<deque>
#include<vector>
#include<map>
#include<set>
#include <utility>
using namespace std;
typedef long long ll ;
#define pii pair<int,int>
const int inf = 0x3f3f3f3f;
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){
if (ch == '-')
f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = (x<<1) + (x<<3) + (ch^48);
ch = getchar();
}
return x * f;
}
void print(__int128 num) {
if(num) {
print(num/10);
putchar(num%10+'0');
}
}
int a[505][505];
int s[505][505];//s[i][j]表示第j列前i个数的前缀和
int main(){
ll n,m,top;
cin>>n>>m>>top;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
scanf("%d",&a[i][j]);
s[i][j]=a[i][j]+s[i-1][j];
}
}
ll ans=0;
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j++){
ll sum=0;
for(int l=1,r=1;r<=m;r++){
sum=sum+s[j][r]-s[i-1][r];//-s[i-1][r]不是-s[i][r];!!!!
while(l<=r&&sum>top){
sum=sum-(s[j][l]-s[i-1][l]);
l++;
}
if(l<=r&&sum<=top){
ans=ans+r-l+1;
}
}
}
}
printf("%lld\n",ans);
return 0;
}
G:积木画
H:扫雷:
这题和李白打酒一样还是dp,但是状态表示和状态转移方程好难写啊,看了题解才会的,菜鸡表示好难!!!
状态表示:dp[i][0]表示前i行已经满了,i+1没有木块,dp[i]][1]表示前i行满了,i+1行有一个木块
a[i][0]=(a[i-1][0]+a[i-2][1]+a[i-2][0])%mod;(注意a[i-2][0]拼a[i][0]只有一种情况,就是两块木块横着放)
a[i][1]=(2*a[i-1][0]+a[i-1][1])%mod;
代码如下:
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<stack>
#include<deque>
#include<vector>
#include<map>
#include<set>
#include <utility>
using namespace std;
typedef long long ll ;
#define pii pair<int,int>
const int inf = 0x3f3f3f3f;
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){
if (ch == '-')
f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = (x<<1) + (x<<3) + (ch^48);
ch = getchar();
}
return x * f;
}
void print(__int128 num) {
if(num) {
print(num/10);
putchar(num%10+'0');
}
}
ll a[10000005][4];//a[i][0]表示前i列已经满了,a[i][1]表示前i列已经满了,第i+1列有一个方格
int n;
ll mod=1000000007;
int main(){
scanf("%d",&n);
a[1][0]=1;
a[1][1]=2;
a[2][0]=2;
a[2][1]=4;
if(n<=2){
printf("%lld\n",a[n][0]);
}else{
for(int i=3;i<=n;i++){
a[i][0]=(a[i-1][0]+a[i-2][1]+a[i-2][0])%mod;
a[i][1]=(2*a[i-1][0]+a[i-1][1])%mod;
}
printf("%lld\n",a[n][0]%mod);
}
return 0;
}
咖啡老师说的对,如果没思路,就暴力开始搜素,把火箭能炸到的炸雷入队列,虽然超时,但可以骗分
#include <bits/stdc++.h>
struct node
{
int x,y,d;
} a[100000],p[100010];
int vis[100000],t,n;
long long juli(int x,int y,int xx,int yy)
{
return (long long)((x-xx)*(x-xx)+(y-yy)*(y-yy));
}
int main()
{
int i,j,m;
scanf("%d%d",&n,&m);
for(i=1; i<=n; i++)
scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].d);
for(i=1; i<=m; i++)
scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].d);
int l=0,r=m+1;
while(++l<r)
{
for(i=1; i<=n; i++)
{
//用vis来记录是否已经入队
if(vis[i]==0&&juli(p[l].x,p[l].y,a[i].x,a[i].y)<=(long long )p[l].d*p[l].d)//要是在范围内就加入队列变成排雷火箭
{
vis[i]=1;
t++;
p[r++]=a[i];
}
}
}
printf("%d",t);
return 0;
}
正确做法:二分+搜索
设火箭的爆炸范围为r,坐标为(x,y,z),那么将炸雷的坐标按x从小到大排序,然后将x1<x-r和x1>x+r的排除
bfs和dfs都行
bfs:
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<stack>
#include<deque>
#include<vector>
#include<map>
#include<set>
#include <utility>
//二分+搜索
using namespace std;
typedef long long ll ;
#define pii pair<int,int>
const int inf = 0x3f3f3f3f;
inline int read() {
int x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-')
f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
void print(__int128 num) {
if (num) {
print(num / 10);
putchar(num % 10 + '0');
}
}
struct Node {
ll x, y, r;
} a[50004], b[50004];
bool cmp(Node a, Node b) {
return a.x < b.x;
}
int ans = 0;
int n, m;
double dis(ll x1, ll x2, ll y1, ll y2) {
return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
int vis[50005];
void bfs(int x, int y, int r) {
queue<Node>q;
q.push({x, y, r});
while (!q.empty()) {
Node u = q.front();
q.pop();
int left = 1;
int right = n;
int lmid, rmid;
while (left <= right) { //确定左端点,
int mid = (left + right) / 2;
if (a[mid].x < u.x - u.r) {
left = mid + 1;
} else {
if (a[mid].x == u.x - u.r) {
break;
} else {
right = mid - 1;
}
}
}
lmid = left;
left = 1, right = n;
while (left <= right) { //确定右端点
int mid = (left + right) / 2;
if (a[mid].x < u.x + u.r) {
left = mid + 1;
} else {
if (a[mid].x == u.x + u.r) {
break;
} else {
right = mid - 1;
}
}
}
rmid = right;
for (int i = lmid; i <= rmid; i++) {
if (vis[i] == 0 && dis(a[i].x, u.x, a[i].y, u.y) <= u.r) {
ans++;
vis[i] = 1;
q.push({a[i].x, a[i].y, a[i].r});
}
}
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%lld%lld%lld", &a[i].x, &a[i].y, &a[i].r);
}
for (int i = 1; i <= m; i++) {
scanf("%lld%lld%lld", &b[i].x, &b[i].y, &b[i].r);
}
sort(a + 1, a + 1 + n, cmp);
for (int i = 1; i <= m; i++) {
bfs(b[i].x, b[i].y, b[i].r);
}
printf("%d\n", ans);
return 0;
}
dfs:
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<stack>
#include<deque>
#include<vector>
#include<map>
#include<set>
#include <utility>
//二分+搜索
using namespace std;
typedef long long ll ;
#define pii pair<int,int>
const int inf = 0x3f3f3f3f;
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){
if (ch == '-')
f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = (x<<1) + (x<<3) + (ch^48);
ch = getchar();
}
return x * f;
}
void print(__int128 num) {
if(num) {
print(num/10);
putchar(num%10+'0');
}
}
struct Node{
ll x,y,r;
}a[50004],b[50004];
bool cmp(Node a,Node b){
return a.x<b.x;
}
int ans=0;
int n,m;
double dis(ll x1,ll x2,ll y1,ll y2){
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
int vis[50005];
void dfs(int x,int y,int r){
int left=1;
int right=n;
int lmid,rmid;
while(left<=right){//确定左端点,
int mid=(left+right)/2;
if(a[mid].x<x-r){
left=mid+1;
}else{
if(a[mid].x==x-r){
break;
}else{
right=mid-1;
}
}
}
lmid=left;
left=1,right=n;
while(left<=right){//确定右端点
int mid=(left+right)/2;
if(a[mid].x<x+r){
left=mid+1;
}else{
if(a[mid].x==x+r){
break;
}else{
right=mid-1;
}
}
}
rmid=right;
for(int i=lmid;i<=rmid;i++){
if(vis[i]==0&&dis(a[i].x,x,a[i].y,y)<=r){
ans++;
vis[i]=1;
dfs(a[i].x,a[i].y,a[i].r);
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].r);
}
for(int i=1;i<=m;i++){
scanf("%lld%lld%lld",&b[i].x,&b[i].y,&b[i].r);
}
sort(a+1,a+1+n,cmp);
for(int i=1;i<=m;i++){
dfs(b[i].x,b[i].y,b[i].r);
}
printf("%d\n",ans);
return 0;
}
I:李白打酒加强版:
我感觉dp最难的就是状态表示和状态转移方程,然后我就推不出,真的难受,看了答案之后就恍然大悟了;
dp[i][j][k]为遇到了i次店,j次花,还有k斗酒
因为最后一次遇到的是花,所以答案为dp[n][m-1][0]
当遇到店时,dp[i][j][k]+=dp[i-1][j][k/2]所以i>0而且k为偶数且不为0
当遇到花时,dp[i][j][k]+=dp[i][j-1][k+1](确保j>0)
初始化dp[0][0][2]=1
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<stack>
#include<deque>
#include<vector>
#include<map>
#include<set>
#include <utility>
using namespace std;
typedef long long ll ;
#define pii pair<int,int>
const int inf = 0x3f3f3f3f;
inline int read(){
int x = 0, f = 1;
char ch = getchar();
while(ch < '0' || ch > '9'){
if (ch == '-')
f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = (x<<1) + (x<<3) + (ch^48);
ch = getchar();
}
return x * f;
}
void print(__int128 num) {
if(num) {
print(num/10);
putchar(num%10+'0');
}
}
ll mod=1000000007;
int n,m;
ll dp[105][105][105];//dp[i][j][k]遇到了i次店,j次花还有k斗酒
int main(){
scanf("%d%d",&n,&m);
dp[0][0][2]=1;
for(int i=0;i<=n;i++){
for(int j=0;j<=m-1;j++){
for(int k=0;k<=m;k++){
if(k%2==0&&i)dp[i][j][k]=(dp[i][j][k]+dp[i-1][j][k/2])%mod;
if(j)dp[i][j][k]=(dp[i][j][k]+dp[i][j-1][k+1])%mod;
}
}
}
printf("%lld\n",dp[n][m-1][1]);
return 0;
}
今天是2023.4.7了,明天就考试了,希望有个好结果吧,写完就睡了,有点累了。