2022牛客寒假算法基础集训营1

C-Baby’s first attempt on CPU

D-牛牛做数论

已知欧拉函数 ϕ ( x ) \phi(x) ϕ(x)是满足 1 ≤ y ≤ x 1\leq y\leq x 1≤y≤x且 g c d ( x , y ) = 1 gcd(x,y)=1 gcd(x,y)=1的y的个数。例如 ϕ ( 6 ) = 2 , ϕ ( 5 ) = 4 \phi(6)=2,\phi(5)=4 ϕ(6)=2,ϕ(5)=4
现定义 H ( x ) = ϕ ( x ) x H(x)= \frac{\phi(x)}{x} H(x)=xϕ(x)​,其定义域为 x ≥ 2 x\geq2 x≥2且是正整数。
现给定一个n,需解决以下两个问题:
1、求满足 x 0 ∈ [ 2 , n ] x_0\in [2,n] x0​∈[2,n],使得 H ( x 0 ) H(x_0) H(x0​)取到 H ( x ) H(x) H(x)在 [ 2 , n ] [2,n] [2,n]的最小值。若存在多个这样的 x 0 x_0 x0​,输出最小的一个。
2、求满足 x 0 ∈ [ 2 , n ] x_0\in [2,n] x0​∈[2,n],使得 H ( x 0 ) H(x_0) H(x0​)取到 H ( x ) H(x) H(x)在 [ 2 , n ] [2,n] [2,n]的最大值。若存在多个这样的 x 0 x_0 x0​,输出最大的一个。

思路:
欧拉函数通式:

E-炸鸡块君的高中记忆

现有n个人要进入学校,但只仅有m( m ≤ n m\leq n m≤n)张校园卡,需先让m个人带校园可进入学校,再派一个人带着所有m张校园卡出来,重复上述过程,直到所有人进入学校。若进出学校均需一个单位的时间,试求所有人都进入学校最少需要花费几个单位的时间。

思路:
1、数据范围: 1 ≤ m ≤ n ≤ 1 0 9 1\leq m\leq n\leq 10^9 1≤m≤n≤109,直接暴力模拟会超时!
2、假设指定一个人为送卡的工具人,则m张卡每次可以送m-1个人进入校园,该工具人一共需要送n-1个人进入校园,那么总共需要送 ⌈ ( n − 1 ) / ( m − 1 ) ⌉ \lceil(n-1)/(m-1)\rceil ⌈(n−1)/(m−1)⌉次(进入校园的次数);工具人送卡还需出校园( ⌈ ( n − 1 ) / ( m − 1 ) ⌉ − 1 \lceil(n-1)/(m-1)\rceil-1 ⌈(n−1)/(m−1)⌉−1)次。进出校园次数等同于所需时间,则总共所需时间即为:
2 × ⌈ ( n − 1 ) / ( m − 1 ) ⌉ − 1 2\times \lceil(n-1)/(m-1)\rceil-1 2×⌈(n−1)/(m−1)⌉−1
3、注意特判:
当 m = n m=n m=n时,输出为1;
当 m = 1 < n m=1<n m=1<n时,输出-1;(一张卡只能让最多一个人进入校园)

#include<bits/stdc++.h>
using namespace std;
int main(){int t;scanf("%d",&t);while(t--){int n,m;scanf("%d%d",&n,&m);if(n==m) printf("1\n");else if(m==1) printf("-1\n");else{int num=ceil((n-1)*1.0/(m-1));printf("%d\n",2*num-1);}}return 0;
}

补充:
n个东西,每次减少m个,减少多少次能变成0?
n / m + ( n % m ! = 0 ) n/m + (n\%m !=0) n/m+(n%m!=0)

J-小朋友做游戏

某班级有A个安静的小朋友和B个闹腾的小朋友,需从中选出n个人来做游戏。该游戏需要小朋友们手拉手围城一个圆圈,但是两个闹腾的小朋友不能在圆圈中紧挨,否则游戏无法进行。已知每位小朋友都有其各自的幸福度v,每选中一位小朋友,都会使得班级的幸福度增加v。试求一种选择n位小朋友的方法,使班级的幸福度达到最大。

思路:
1、“两个闹腾的小朋友不能在圆圈中紧挨”,也就是说闹腾的小朋友必须由安静小朋友隔开,相当于闹腾的小朋友总数量不能超过n/2;
2、将全体小朋友按照幸福度从高到低排序,依次遍历,如果当前小朋友是安静的,则选择;如果当前小朋友是闹腾的,且目前被选择的闹腾的小朋友总数量不超过n/2,则选择。
3、注意特判,若安静小朋友“不够”,则输出-1。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
struct stu{int v;int sta;
}s[maxn];
bool cmp(stu a,stu b){return a.v>b.v;
}
int main(){int t;scanf("%d",&t);while(t--){int a,b,n;scanf("%d%d%d",&a,&b,&n);for(int i=0;i<a;i++){scanf("%d",&s[i].v);s[i].sta=1;//安静小朋友标为1}for(int i=a;i<a+b;i++){scanf("%d",&s[i].v);s[i].sta=0;//闹腾小朋友标为0}sort(s,s+a+b,cmp);int numb=min(b,n/2);//闹腾小朋友的最大可选数量int numa=n-numb;//至少需要选择的安静小朋友//cout<<"numa: "<<numa<<"  numb: "<<numb<<endl;if(numa>a) printf("-1\n");else{int ans=0;numa=0;numb=0;for(int i=0;i<a+b;i++){if(s[i].sta==1){numa++;ans+=s[i].v;}if(numb<n/2&&s[i].sta==0){numb++;ans+=s[i].v;}if(numa+numb>=n) break;}printf("%d\n",ans);}   }       return 0;
}

L-牛牛学走路(签到)

签到题,按照题目意思,遍历字符串,计算最远距离即可。

#include<bits/stdc++.h>
using namespace std;int main(){int t;cin>>t;while(t--){int l;cin>>l;char ch;double x=0,y=0;double mmax=0;for(int i=0;i<l;i++){cin>>ch;if(ch=='U') y++;else if(ch=='D') y--;else if(ch=='L') x--;else if(ch=='R') x++;mmax=max(mmax,x*x+y*y);}printf("%.10lf\n",sqrt(mmax));}return 0;
}

补充知识:
c++ hypot()函数
hypot()函数是cmath标头的库函数,用于查找给定数字的斜边,接受两个数字并返回斜边的计算结果,即sqrt(x * x + y * y)。

2022牛客寒假算法基础集训营2

C-小沙的杀球

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
typedef long long ll;
int main(){ll x,a,b;cin>>x>>a>>b;string s;cin>>s;ll ans=0;for(int i=0;i<s.length();i++){if(s[i]=='1'&&x-a>=0){x-=a;ans++;}else if(s[i]=='0'||x-a<0) x+=b;}cout<<ans<<endl;return 0;
}

H-小沙的数数

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
const ll mod=1e9+7;
ll binaryPow(ll a, ll b){ll ans = 1;while(b > 0){if(b & 1) ans = ans * a % mod;a = a * a % mod;b >>= 1; } return  ans%mod;
}
ll cnt(ll n){int count=0;while(n > 0) {  count++;  n&=(n-1);  }  return count;
}
int main(){ll n,m;scanf("%lld %lld",&n,&m);n%=mod;m=cnt(m)%mod;ll ans=binaryPow(n,m);printf("%lld\n",ans);return 0;
}

K-小沙的步伐

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int ans[10];
int main(){string s;cin>>s;int num=0;for(int i=0;i<s.length();i++){ans[s[i]-'0']++;if(s[i]=='5') num++;}ans[5]=s.length()-num;for(int i=1;i<=9;i++){cout<<ans[i];if(i==9) cout<<endl;else cout<<" ";}
}

2022牛客寒假算法基础集训营3

B-智乃买瓜(分组背包)

共有 n n n个不同的西瓜,第 i i i个西瓜的重量为 w i w_i wi​。买瓜时,对于每个西瓜都可以有三种不同的决策:
1、购买一整个重量为 w i w_i wi​的西瓜
2、把瓜劈开,购买半个重量为 w i 2 \frac{w_i}{2} 2wi​​的西瓜
3、不进行购买操作
现保证所有瓜的重量都是一个正偶数,求购买重量分别为 k = 1 , 2 , 3 ⋯ m k=1,2,3\cdots m k=1,2,3⋯m的西瓜各有多少种购买方案。由于数字可能很大,输出方案数对 1 0 9 + 7 10^9+7 109+7取余后的结果。

背包问题求方案数
思路:
d p [ i ] [ j ] dp[i][j] dp[i][j]表示现在放到第 i i i个西瓜,且当前的重量为 j j j(恰好装满容量为 j j j的背包)。
显然 d p [ 0 ] [ 0 ] = 1 dp[0][0]=1 dp[0][0]=1(不进行购买操作)
状态转移方程:

根据 j j j值得大小采取相应的策略。
分组背包=

二维数组做法

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e4+10;
const ll mod=1e9+7;
int w[maxn];
int dp[maxn][maxn];
int main(){int n,m;scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){scanf("%d",&w[i]);}dp[0][0]=1;for(int i=1;i<=n;i++){for(int j=0;j<=m;j++){if(j>=w[i]) dp[i][j]=(dp[i-1][j-w[i]]+dp[i-1][j-w[i]/2]+dp[i-1][j])%mod;//决策1、2、3else if(j>=w[i]/2&&j<w[i]) dp[i][j]=(dp[i-1][j-w[i]/2]+dp[i-1][j])%mod;//决策2,3else dp[i][j]=dp[i-1][j];//决策3}}for(int i=1;i<=m;i++){printf("%d ",dp[n][i]);}return 0;
}

滚动数组做法(一维)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e4+10;
const ll mod=1e9+7;
int w[maxn];
int dp[maxn];
int main(){int n,m;scanf("%d%d",&n,&m);dp[0]=1;for(int i=1;i<=n;i++){int x;scanf("%d",&x);for(int j=m;j>=1;j--){if(j>=x/2) dp[j]=(dp[j]+dp[j-x/2])%mod;if(j>=x) dp[j]=(dp[j]+dp[j-x])%mod; }}for(int i=1;i<=m;i++){printf("%d ",dp[i]);}return 0;
}

C-智乃买瓜(another version)

C题是B题输入输出的倒置。
给出购买各种重量西瓜的方案数对 1 0 9 + 7 10^9+7 109+7取余后的结果。求原来西瓜的数目及每个西瓜的重量。

思路:
逆向思维,如何将放入背包中的物品取出来?

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
const int mod=1000000007;
const int INF=0x3f3f3f3f;
typedef long long ll;
int dp[N];
vector<int>ans;
int main(){int m;scanf("%d",&m);dp[0]=1;for(int i=1;i<=m;i++){int x;scanf("%d",&x);dp[i]=x;}for(int i=1;i<=m;i++){while(dp[i]){ans.push_back(2*i);for(int j=0;j<=m;j++){if(j+i<=m) dp[i+j]-=dp[j];if(j+i<=m&&dp[i+j]<0) dp[i+j]+=mod;if(j+2*i<=m) dp[2*i+j]-=dp[j];if(j+2*i<=m&&dp[2*i+j]<0) dp[2*i+j]+=mod;}}}printf("%d\n",ans.size());for(int i=0;i<ans.size();i++){printf("%d",ans[i]);if(i==ans.size()-1) printf("\n");else printf(" ");}return 0;
}

L-智乃的数据库

现给定一个由N条记录、M个字段组成的数据库表。模拟实现查询语句“SELECT COUNT(∗) FROM Table GROUP BY…;”

思路:
想清楚其实就是只考虑group by选定的那几列,其他的不管。
然后统计分组:有多少行是一模一样的,其数量为多少。
对于每一行的处理,可以直接变为字符串,map统计即可。
也可以使用map<vector,int>

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e4+10;
const ll mod=1e9+7;
map<string,int>mp;
vector<int>c[maxn];
int main(){int n,m;cin>>n>>m;for(int i=0;i<m;i++){string s;cin>>s;mp[s]=i;}for(int i=0;i<n;i++){for(int j=0;j<m;j++){int x;cin>>x;c[i].push_back(x);}}string s;getchar();getline(cin,s);int k=s.find("Y");string t="";vector<int>pos;map<vector<int>,int>ans;for(int i=k+2;i<s.length();i++){if(s[i]==','||s[i]==';'){pos.push_back(mp[t]);t="";} else t+=s[i];}vector<int>row;for(int i=0;i<n;i++){row.clear();for(auto j:pos){row.push_back(c[i][j]);} ans[row]++;}cout<<ans.size()<<endl;for(auto j:ans){cout<<j.second<<" ";}return 0;
}

I-智乃的密码(尺取)

某网站密码必须符合以下条件:
1、密码是仅包含大小写英文字母、数字、特殊符号的字符串
2、密码的长度不少于L个字符,并且不多于R个字符。
3、密码中应该至少包括1)大写英文字母 2)小写英文字母 3)数字 4)特殊符号这四类字符中的三种。
现有一个长度大小为N的字符串S,求S串中有多少子串是一个符合条件的密码。

思路:
数据范围: 1 ≤ N ≤ 1 0 5 , 1 ≤ L ≤ R ≤ N 1\leq N\leq 10^5,1\leq L\leq R \leq N 1≤N≤105,1≤L≤R≤N
字符串长度不超过1e5,所以可以遍历。
尺取
对于每一个位置,需要计算从该位置往后 L L L到 R R R的区间内有多少个满足条件(至少有3中类型的字符)的字符串。

注意开longlong,不然过不了。。。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
int main(){int n,l,r;cin>>n>>l>>r;string s;cin>>s;int f1=0,f2=0,f3=0,f4=0;if(s[0]>='A'&&s[0]<='Z') f1++;else if(s[0]>='a'&&s[0]<='z') f2++;else if(s[0]>='0'&&s[0]<='9') f3++;else f4++;int j=0;ll ans=0;//开longlong!!!for(int i=0;i<n;i++){//当前位置 while(j-i+1<=r&&j<n){if(j-i+1>=l){//字符串长度满足条件 if(f1&&f2&&f3||f1&&f2&&f4||f1&&f3&&f4||f2&&f3&&f4){//字符类型满足条件 ans+=min(i+r-1,n-1)-j+1;//对于当前位置,已经可以计算出满足条件的字符串个数,无需继续遍历 break;}}j++;//字符统计 if(s[j]>='A'&&s[j]<='Z') f1++;else if(s[j]>='a'&&s[j]<='z') f2++;else if(s[j]>='0'&&s[j]<='9') f3++;else f4++;}if(j==n) break;//没有满足条件的字符串,直接结束 //考虑到第i个字符是独一个的情况,将其“贡献”减去 if(s[i]>='A'&&s[i]<='Z') f1--;else if(s[i]>='a'&&s[i]<='z') f2--;else if(s[i]>='0'&&s[i]<='9') f3--;else f4--;  }cout<<ans<<endl;
}

E-智乃的数字积木

共有N块积木,每一块积木都有自己的颜色以及数字,这N块积木颜色的范围从1到M,数字的范围从0到9。将积木从左到右排成一排,可形成一个大整数。
已知可在任意时刻无限次的交换相邻且同色的数字积木,同时还可以进行k次操作,每次操作都选中两种颜色P、Q,然后将所有颜色为P的积木染成颜色Q。
求进行K次染色操作之前,以及每次染色之后能够通过交换得到的最大正整数对 1 0 9 + 7 10^9+7 109+7取余数的结果。

思路:
由于数据范围并不大,所以按照题意模拟:
每次取颜色相同的一整段按数字从大到小排序。

数组切段

   for(int l=1,r=1;l<=n;l=r+1,r=l){while(r<n&&(a[l]==a[r+1])) r++;printf("[%d,%d] ",l,r);}

匿名函数

sort需要传排序函数的时候,如果cmp函数没有重复利用的必要,可以使用匿名函数在调用sort函数时直接定义。这样做的好处是在面向过程的编程中,不会破坏编程的连续性,你可以顺着在一个函数过程中直接写,不用在跳出函数外部额外写一个函数,可以增加写代码的速度

sort(a + 1, a + 1 + n, [](const int&A, const int&B) {return A > B;
});

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const ll mod=1e9+7;
int n,m,k;
char s[maxn];
int col[maxn];
ll cal(){for(int l=1,r=1;l<=n;l=r+1,r=l){while(r<n&&(col[l]==col[r+1])) r++;//printf("[%d,%d] ",l ,r);sort(s+l,s+r+1,[](const char&A, const char&B) {return A > B;});}ll ans=0;for(int i=1;i<=n;i++){ans=ans*10+(s[i]-'0');ans%=mod;} return ans;
}
void change(int u,int v){for(int i=1;i<=n;i++){if(col[i]==u) col[i]=v;}return;
}
int main(){scanf("%d %d %d",&n,&m,&k);scanf("%s",s+1);for(int i=1;i<=n;i++){scanf("%d",&col[i]);}printf("%lld\n",cal());while(k--){int u,v;scanf("%d %d",&u,&v);change(u,v);printf("%lld\n",cal());}return 0;
}

2022牛客寒假算法基础集训营4

A-R

一长度为 n n n的字符串,仅由大写字母组成。现需要从中取出一个连续子串,该子串包含至少 k k k个’R’字符,且不能包含’P’字符。求有多少种合法的方案可以取到。
1 ≤ n ≤ 200000 1\leq n\leq 200000 1≤n≤200000
1 ≤ k ≤ 20 1\leq k\leq 20 1≤k≤20

思路:
这道题跟上一场密码那道题类似,都可以用尺取法来做。
由于所取子串不能包含’P’字符,所以我们取子串的起点应该从第一个’R’字符开始。(也就是说前缀’P’不要管)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const ll mod=1e9+7;
string s;
vector<int>pos;//记录所有P字符的位置
int main(){int n,k;cin>>n>>k>>s;int mmax=0;//最后一个P出现的位置int cnt=0;int st=0;//第一个R出现的位置for(int i=0;i<s.size();i++){if(s[i]=='R'&&cnt==0){cnt++;st=i;}if(s[i]=='P') {mmax=max(mmax,i);pos.push_back(i);}}int R=1,P=0;//统计P、R字符的数量int j=st;ll ans=0;for(int i=st;i<n;i++){while(j<n){if(j-i+1>=k){//当前子串长度大于等于k时,进一步判断是否能满足条件if(R>=k&&P==0){//当前子串满足条件int len=0;if(j<=mmax){//后面还有P字符int r=upper_bound(pos.begin(),pos.end(),j)-pos.begin();//cout<<"i: "<<i<<" j: "<<j<<" r: "<<pos[r]<<" len: "<<pos[r]-j<<endl;len=pos[r]-j;}else len=n-j;//后面没有P字符ans+=len;break;}}j++;if(s[j]=='R') R++;if(s[j]=='P') P++;if(P){//一旦出现P字符,直接更新位置到P字符的下一位置i=j;R=0;break;}}if(j==n) break;if(s[i]=='R') R--;else if(s[i]=='P') P--;}cout<<ans<<endl;return 0;
}

C-蓝彗星

有两种不同颜色的彗星:红彗星和蓝彗星。每颗彗星会在某一时间段出现 t t t秒然后消失。
求能看到蓝彗星且看不到红彗星的时间总共有多少秒。

差分

一维差分:给区间 [ l , r ] [l,r] [l,r]中的每个数加上 x x x:a[l]+=c,a[r+1]-=x;


每颗星星的出现时间都是一个区间,只需要借助把对应区间内的时间点都带上标记,最后通过前缀和就可以找到只有蓝彗星出现而没有红彗星出现的时间段。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
typedef long long ll;
ll sumb[maxn],sumr[maxn];
int main(){int n,t; cin>>n>>t;string s;cin>>s;for(int i=0;i<n;i++){int x;cin>>x;if(s[i]=='B') sumb[x]++,sumb[x+t]--;//差分else sumr[x]++,sumr[x+t]--;}ll ans=0;for(int i=1;i<maxn;i++){sumb[i]=sumb[i-1]+sumb[i];sumr[i]=sumr[i-1]+sumr[i];if(sumb[i]&&(!sumr[i])) ans++; }cout<<ans<<endl;return 0;
}

D-雪色光晕

二维平面中,点A的初始坐标为 ( x 0 , y 0 ) (x_0,y_0) (x0​,y0​),该点每秒有一个方向向量 ( x i , y i ) (x_i,y_i) (xi​,yi​),会沿着该方向直线移动一秒(一秒后A点坐标变为 ( x 0 + x i , y 0 + y i ) (x_0+x_i,y_0+y_i) (x0​+xi​,y0​+yi​)。另有一不动点 P ( x , y ) P(x,y) P(x,y)。求在点 A A A移动过程中,两点之间的最短距离是多少?

点到线段的最短距离

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
const int mod=1000000007;
const int INF=0x3f3f3f3f;
typedef long long ll;
struct Point{double x,y;
};
double getDis(Point A ,Point B){return (A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y);
}
double disMin(Point A,Point B,Point P){double r=((P.x-A.x)*(B.x-A.x)+(P.y-A.y)*(B.y-A.y))/getDis(A,B);if(r<=0) return sqrt(getDis(A,P));else if(r>=1) return sqrt(getDis(B,P));else{double AC=r*sqrt(getDis(A,B));return sqrt(getDis(A,P)-AC*AC);}
}
int main(){int n;scanf("%d",&n);Point A,B,P;  scanf("%lf%lf%lf%lf",&A.x,&A.y,&P.x,&P.y);double ans=INF;for(int i=0;i<n;i++){double dx,dy;scanf("%lf%lf",&dx,&dy);B.x=A.x+dx;B.y=A.y+dy;ans=min(ans,disMin(A,B,P));A=B;}printf("%.10lf\n",ans);return 0;
}

E-真假签到题

long long f(long long x){
if(x==1)return 1;
return f(x/2)+f(x/2+x%2);
}
给定一个正整数 x x x,求 f ( x ) f(x) f(x)的值,其中 1 ≤ x ≤ 1 0 18 1\leq x\leq 10^{18} 1≤x≤1018

思路:
直接用题目所给函数,暴力打表,发现: f ( x ) = x f(x)=x f(x)=x

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const ll mod=1e9+7;
int main(){ll x;cin>>x;cout<<x<<endl;return 0;
}

F-小红的记谱法

论读懂题目的重要性!

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
map<char,int>mp;
int main(){mp['C']=1;mp['D']=2,mp['E']=3,mp['F']=4,mp['G']=5,mp['A']=6,mp['B']=7;string s;cin>>s;int numd=0;int numm=0;for(int i=0;i<s.size();i++){if(isalpha(s[i])){cout<<mp[s[i]];char ch;if(numm==numd) continue;else if(numm>numd) ch='*';else ch='.';for(int i=0;i<max(numm,numd)-min(numm,numd);i++) cout<<ch;}if(s[i]=='>') numm++;if(s[i]=='<') numd++;}
}

H-真真真真真签到题

小红和紫被困在一个正方体的内部。紫先选择了一个位置,然后小红选择一个位置。紫希望离小红尽可能近,小红希望离紫尽可能远。两人都会选择最优策略。
已知她们最终的距离为 x x x 。小红想知道正方体的体积是多少?

思路:
从出题人给出的说明中也可以看出,最优策略即为:紫选择站在正方体中心,小红站在正方体的一个顶点上。则她们的距离即为该正方体的体对角线长度的一半。
正方体体对角线长度: 3 a \sqrt{3}a 3 ​a(a为正方体边长)
由 3 a = 2 x \sqrt{3}a=2x 3 ​a=2x,即可求得边长,进而求得体积。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const ll mod=1e9+7;
int main(){double x;scanf("%lf",&x);double a=2.0/sqrt(3)*x;printf("%.10lf\n",pow(a,3));return 0;
}

I-爆炸的符卡洋洋洒洒

现有 n n n张符卡可供选择,每种符卡最多只能选择一次,每个符卡的魔力消耗为 a i a_i ai​,威力为 b i b_i bi​。若将多个符卡进行组合,则可以发动一个组合魔法。组合魔法的魔力消耗为选择的符卡的魔力消耗的总和,其威力为选择的符卡的威力的总和。
必须保证最终符卡的魔力消耗总和为k的倍数,否则将受到魔力反噬而发动魔法失败。问能组合发动的组合魔法最大的威力是多少?若无论如何也组合不了能发动的魔法,则输出-1。

思路:
01背包变种
传统01背包模型:每个物品有一个重量和一个价值,问总重量不超过 x x x能取到的最大价值。
–>维护前 i i i个物品重量为 j j j的最大值
此题变为:总重量为 k k k的倍数时能取得的最大价值是多少
->维护前 i i i个物品重量 m o d mod mod k k k为 j j j的最大值
转移方程:
d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ ( j − w [ i ] % k + k ) % k ] + v [ i ] ) dp[i][j]=max(dp[i-1][j],dp[i][(j-w[i]\%k+k)\%k]+v[i]) dp[i][j]=max(dp[i−1][j],dp[i][(j−w[i]%k+k)%k]+v[i])
负数取模技巧:先转为绝对值小于 k k k的负数,然后加 k k k再模 k k k。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e3+10;
typedef long long ll;
ll w[maxn];
ll v[maxn];
ll dp[maxn][maxn];
int main(){int n,k;cin>>n>>k;for(int i=1;i<=n;i++){cin>>w[i]>>v[i];}for(int i=0;i<=n;i++){for(int j=0;j<=k;j++){dp[i][j]=-1e16;}}dp[0][0]=0;for(int i=1;i<=n;i++){for(int j=0;j<k;j++){//不选dp[i][j]=dp[i-1][j];//选dp[i][j]=max(dp[i][j],dp[i-1][(j-w[i]%k+k)%k]+v[i]); }}if(dp[n][0]<=0) cout<<"-1";else cout<<dp[n][0];return 0;
}

J-区间合数的最小公倍数

求区间 [ l , r ] [l,r] [l,r]内的所有合数的最小公倍数是多少?由于答案可能过大,对1000000007取模。
若区间内没有合数,输出-1。
1 ≤ l ≤ r ≤ 30000 1\leq l\leq r\leq 30000 1≤l≤r≤30000

数论-最大公约数和最小公倍数

已知: a = p 1 e 1 p 2 e 2 ⋯ p n e n a=p_1^{e_1}p_2^{e_2}\cdots p_n^{e_n} a=p1e1​​p2e2​​⋯pnen​​, b = p 1 f 1 p 2 f 2 ⋯ p n f n b=p_1^{f_1}p_2^{f_2}\cdots p_n^{f_n} b=p1f1​​p2f2​​⋯pnfn​​
则 g c d ( a , b ) = p 1 m i n ( e 1 , f 1 ) p 2 m i n ( e 2 , f 2 ) ⋯ p n m i n ( e n , f n ) gcd(a,b)=p_1^{min(e_1,f_1)}p_2^{min(e_2,f_2)}\cdots p_n^{min(e_n,f_n)} gcd(a,b)=p1min(e1​,f1​)​p2min(e2​,f2​)​⋯pnmin(en​,fn​)​
l c m ( a , b ) = p 1 m a x ( e 1 , f 1 ) p 2 m a x ( e 2 , f 2 ) ⋯ p n m a x ( e n , f n ) lcm(a,b)=p_1^{max(e_1,f_1)}p_2^{max(e_2,f_2)}\cdots p_n^{max(e_n,f_n)} lcm(a,b)=p1max(e1​,f1​)​p2max(e2​,f2​)​⋯pnmax(en​,fn​)​

1、对合数质因数分解
2、求每个质因数的幂的最大值

#include<bits/stdc++.h>
using namespace std;
const int N=3e4+10;
const int mod=1000000007;
typedef long long ll;
int cnt;
bool vis[N];
int pri[N];
unordered_map<int,int>mp;
//欧拉筛
void oulasai(){for(int i=2;i<=N;i++){if(!vis[i]) pri[cnt++]=i;for(int j=0;j<cnt;j++){if(i*pri[j]>N) break;vis[i*pri[j]]=true;if(i%pri[j]==0) break;}}
}
ll ksm(ll a,ll b){ll ans=1;while(b){if(b&1) ans=ans*a%mod;a=a*a%mod;b>>=1;}return ans%mod;
}
int main(){oulasai();ll l,r;scanf("%lld%lld",&l,&r);int flag=0;for(int i=l;i<=r;i++){if(vis[i]){flag=1;int x=i;for(int j=0;2*pri[j]<=i;j++){int num=0;while(x%pri[j]==0&&x){num++;x/=pri[j];}mp[pri[j]]=max(mp[pri[j]],num);//取幂的最大值}}}if(!flag) printf("-1\n");else{ll ans=1;for(int i=0;2*pri[i]<=r;i++){if(mp[pri[i]]!=0){ans=ans*ksm(pri[i],mp[pri[i]])%mod;}}printf("%lld\n",ans);}return 0;
}

K-小红的真真假假签到题题

已知正整数 x x x,现需构造一个正整数 y y y,需满足以下条件:
1、 y y y必须是 x x x的倍数,且 x x x和 y y y不能相等。
2、 x x x在二进制表示下(为一个01串)是 y y y的二进制表示的一个子串。且 x x x和 y y y的二进制表示的1的个数不能相同。
3、 y y y必须为不超过 1 0 19 10^{19} 1019的正整数。
数据范围: 1 ≤ x ≤ 1 0 9 1\leq x\leq 10^9 1≤x≤109

思路:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const ll mod=1e9+7;
string check(ll x){//十进制转二进制string s="";while(x){s+=(x%2)+'0';x/=2;}reverse(s.begin(),s.end());return s;
}
int main(){ll x;scanf("%lld",&x);string s=check(x);//cout<<s<<endl;s+=s;ll ans=0;for(int i=0;i<s.size();i++){//二进制转十进制ans=ans*2+s[i]-'0';}printf("%lld\n",ans);   return 0;
}

2022牛客寒假算法基础集训营5

A-疫苗小孩(枚举,二分查找)

已知打疫苗对手速的影响如下:
1、疫苗最多接种三针,且只有后两针接种才能提升手速,且一天最多只能打一针。
2、后一针疫苗与前一针疫苗之间相隔 k k k天对手速的提升最为明显,每偏离一天效果会相应减少。设定相隔 k k k天对手速的提升为 w w w,如果两针实际相隔了 p p p天,每偏离一天效果减少 q q q,则后一针疫苗实际对人手速的影响为 w − ∣ k − q ∣ ∗ p w-|k-q|*p w−∣k−q∣∗p
现需考虑 n n n天内的疫苗接种,但这 n n n天中有一些比赛不可缺席,所以有比赛的日子是不会接种疫苗的,求如何安排疫苗接种能使手速提升最大。

思路:
最多只能接种三针,意味着可以接种0、1、2、3针,但接种0针和1针,不会提升手速,所以至少要接种两针。
是否必须要接种第三针?
视具体情况而定,如果打第三针还可继续提升手速,那就打,如果无论哪天打,手速都不会再提升,甚至可能减小,那就不打。
枚举打第二针的时间,选出最优方案即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+10;
typedef long long ll;
int pos[maxn];
int main(){ll n,k,w,q;string s;cin>>n>>s>>k>>w>>q;int cnt=0;for(int i=0;i<s.size();i++){if(s[i]=='0') pos[++cnt]=i;}pos[0]=-1000000000;pos[++cnt]=1000000000;可以防止后续二分查找值,找不到的情况ll ans=0;for(int i=1;i<cnt;i++){ll sum=0;int pre=lower_bound(pos,pos+cnt,pos[i]-k)-pos;//第一针int time;if(pre==i) time=abs(k-(pos[i]-pos[pre-1]));else time=min(abs(k-(pos[i]-pos[pre-1])),abs(k-(pos[i]-pos[pre])));sum+=w-time*q;if(sum<0) sum=0;int las=lower_bound(pos,pos+cnt,pos[i]+k)-pos;//第三针if(las==i+1) time=abs(k-(pos[las]-pos[i]));else time=min(abs(k-(pos[las]-pos[i])),abs(k-(pos[las-1]-pos[i])));sum+=w-time*q;if(sum<0) sum=0;ans=max(ans,sum);}cout<<ans<<endl;return 0;
}

D-数位小孩

给出一个区间 [ l , r ] [l,r] [l,r],求这个区间内有多少个数字满足如下条件:
1、每相邻两个数位和为素数
2、其中至少一个数位为1
3、没有前导0
1 ≤ l ≤ r ≤ 1 0 10 1\leq l\leq r\leq 10^{10} 1≤l≤r≤1010

素数环

从1到n这n个整数无重复的围成一个圆环,若其中任意2个相邻的数字相加,结果均为素数,那么这个环就成为素数环。

1 0 10 10^{10} 1010内满足条件的数字数量级为百万(1e7)
dfs出所有素数环
数位只可能是0到9之间的数字

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e7+10;
typedef long long ll;
bool p[100];
ll ans[maxn];
int cnt;
ll l,r;
void dfs(ll x,int flag){if(x>r) return;//超出范围if(flag) ans[cnt++]=x;//int pre=x%10;//上一位for(int i=0;i<=9;i++){//当前位if(p[i+pre]){dfs(x*10+i,flag|(i==1));//至少一个数位1即可}}
}
int main(){scanf("%lld%lld",&l,&r);p[2]=p[3]=p[5]=p[7]=p[11]=p[13]=p[17]=true;//两数位和最大为18for(int i=1;i<=9;i++){//枚举第一位,不能有前导0,从1开始dfs(i,i==1);}ll num=0;for(int i=0;i<cnt;i++){if(ans[i]>=l){//cout<<ans[i]<<endl;num++;} }printf("%lld\n",num);return 0;
}

G-163小孩

从一副去掉大王小王的扑克牌中抽出六张,A,J,Q,K视为1,11,12,13,用这6张牌经过有理数的加减乘除运算(过程中可以出现分数)且每张牌都用到的情况下,如[9,9,9,7,4,7],可以发现(9+9+7)4+97=163或者(9+9+4+9/7)*7=163
如果按照52张取6张的思维去枚举,方案有20358520种,考虑到花色不同但牌型相同的组合其实是一样的,所以实际上对于算163点来讲,不同性质的牌组会比 ( 52 6 ) \tbinom{52}{6} (652​)小很多,计算去重后的这个数字。

思路:
题目可转化成:
有重复数字求组合数
给定一个长度为52的包含重复数字的序列,从中随机选取6个数字,求所有可能方案的数量。
搜索顺序:枚举每一个数,由于有重复的数字,所以不能按照每一个数选与不选枚举,比如有三个数字1,2,2,若按照这种顺序,1,2会枚举到两次,因此应该按照某一个数选几个枚举

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
const ll mod=1e9+7;
int n=52,m=6;
int a[100];
int ans;
int path[100];//记录各种选数方案
void dfs(int x,int num){//x:当前选数开始位置,当前已选数字数量if(num==m){//恰好选够数字 ans++;//方案数加一
//      for (int i = 0; i < m; i++) cout << path[i] << " ";
//      printf("\n");return;} if(num>m) return;if(x>n) return;int y=x;//计算当前数字重复次数 while(y<=n&&a[y]==a[x]) y++;int cnt=y-x;for(int i=0;i<=cnt;i++){//对选多少个当前数字进行枚举,可以不选 //for(int j=x;j<x+i;j++) path[num+j-x]=a[x];dfs(y,num+i);//从下一个不同数字开始选,当前已选num+i个数字 }
}
int main(){for(int i=1;i<=13;i++){for(int j=1;j<=4;j++){a[(i-1)*4+j]=i;}}//sort(a+1,a+n+1); dfs(1,0);cout<<ans<<endl;return 0;
}

I-兔崽小孩(二分、前缀和)

某同学发布了 n n n条说说,且每条说说的发布时间为 t 1 , t 2 , ⋯ , t n ( t i < t i + 1 ) t_1,t_2,\cdots,t_n(t_i<t_{i+1}) t1​,t2​,⋯,tn​(ti​<ti+1​)。现进行 q q q次询问,
每次询问给出该同学的入睡时长 k k k和总共需要的睡眠时间 p p p。判断该同学是否达到了要求的睡眠时间。

思路:
二分+前缀和
该同学只能在两次发说说的时间间隔内睡觉,且只有时间间隔大于入睡时长,才能有效睡着。
所以只要取所有时间间隔大于 k k k的时间段,然后计算总的睡眠时间,并判断是否大于等于 p p p即可。
前缀和处理:
所有时间间隔时长总和-所有时间间隔内所需的入睡时长总和=总睡眠时间
注意:
1、是所有时间间隔内累计的总睡眠时间至少需要达到 p p p
2、不能暴力,会T
3、段错误!!!注意所开数组大小!

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
typedef long long ll;
vector<ll>tim;
ll sum[maxn];
int main(){ll n,q;scanf("%lld%lld",&n,&q);ll x;scanf("%lld",&x);for(int i=1;i<n;i++){ll t;scanf("%lld",&t);tim.push_back(t-x);//发布说说的时间间隔x=t;}sort(tim.begin(),tim.end());//将时间间隔从小到大排序sum[0]=tim[0];for(int i=1;i<tim.size();i++){//前缀和,sum[i]=sum[i-1]+tim[i];}int len=tim.size();for(int i=0;i<q;i++){ll k,p;scanf("%lld%lld",&k,&p);//二分int j=lower_bound(tim.begin(),tim.end(),k)-tim.begin();//寻找第一个大于k的时间间隔出现的位置(该位置后面的所有时间间隔均可)if(j!=len){ll tmp=0;if(j==0) tmp=sum[len-1]-len*k;//注意j=0时的特判,否则waelse tmp=sum[len-1]-sum[j-1]-(len-j)*k;if(tmp>=p) puts("Yes");else puts("No");}else puts("No");//没有找到}return 0;
}

J-三国小孩(签到)

三国杀游戏中所有卡牌及其作用如下所示:
【杀】:对方需使用一张【闪】,否则减少一点体力
【闪】:用来抵消杀
【决斗】:使用后从被决斗的一方开始,需要双方轮流使用一张杀直到有一方不出为止,不出的那方失去一点体力
【桃】:体力值小于等于零时使用,增加一点体力
现在你自己手中有 n n n张【杀】和 m m m张【决斗】,而对方手中有 k k k张未知卡牌。你的体力值无穷大,对方体力值为1,体力值等于0且没有使用【桃】则失败。如果你在某回合没有办法战胜对方,就会自己主动投降。
所有种类的卡牌都没有使用次数限制,但不能重复使用。试问在不知道对方具体手牌的情况下,你一定能够获胜吗?

思路:
关键点:你手中只有【杀】和【决斗】,这两张牌的特点是无论你出那种牌,对方也必须要出一张牌(体力值只有1,防止被杀),所以如果你拥有的牌的总数大于对方,那么不管他有什么牌,都必输。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
typedef long long ll;
int main(){int n,m,k;cin>>n>>m>>k;if(n+m>k) puts("YES");else puts("NO");return 0;
}

2022牛客寒假算法基础集训营6

B-价值序列(思维、计数)

给出长度为 n n n的整数数组 a 1 , a 2 , ⋯ , a n a_1,a_2,\cdots ,a_n a1​,a2​,⋯,an​,定义序列的价值为
∑ i = 1 n − 1 ∣ a i − a i + 1 ∣ \sum_{i=1}^{n-1}|a_i-a_{i+1}| ∑i=1n−1​∣ai​−ai+1​∣
当数组长度为1时,价值认为是0。
求有多少不同的 1 ≤ i 1 < i 2 < . . . < i k ≤ n ( 1 ≤ k ≤ n ) 1\leq i_1<i_2<...<i_k\leq n(1\leq k\leq n) 1≤i1​<i2​<...<ik​≤n(1≤k≤n)满足 a i 1 , a i 2 , ⋯ , a i k a_{i_1},a_{i_2},\cdots ,a_{i_k} ai1​​,ai2​​,⋯,aik​​的价值等于 a 1 , a 2 , ⋯ , a n a_1,a_2,\cdots ,a_n a1​,a2​,⋯,an​的价值。
由于答案可能很大,对答案mod 998244353

思路:
题目其实在问:对原数组进行元素删除操作,是否会对其价值产生影响。
易知,当 n ≤ 2 n\leq 2 n≤2时,删除元素必定会是数组价值减少。
所有连续的相等的元素,可全部删除,价值不变。
其他情况如下:

由此可知,对原数组进行元素删除操作,数组价值必不增。
求使数组价值不变的删除元素方法数,则需找出所有上述元素(删除与否不改变数组的价值)即可。

  • 如果 a i − 1 < a i = . . . = a j < a j + 1 a_{i-1}<a_i=...=a_j<a_{j+1} ai−1​<ai​=...=aj​<aj+1​或 a i − 1 > a i = . . . = a j > a j + 1 ( i > 1 , j < n ) a_{i-1}>a_i=...=a_j>a_{j+1}(i>1,j<n) ai−1​>ai​=...=aj​>aj+1​(i>1,j<n),则区间 [ i , j ] [i,j] [i,j]中的元素可全部删除,价值不变,共有 2 j − i + 1 2^{j-i+1} 2j−i+1中方案。
  • 如果 a i = . . . = a j ≠ a j + 1 a_i=...=a_j\neq a_{j+1} ai​=...=aj​​=aj+1​或 a i − 1 ≠ a i = . . . = a j a_{i-1}\neq a_i=...=a_j ai−1​​=ai​=...=aj​,则区间 [ i , j ] [i,j] [i,j]内的元素必须要保留一个,价值不变,共有 2 j − i + 1 − 1 2^{j-i+1}-1 2j−i+1−1种方案。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
const ll mod=998244353;
ll a[maxn];
ll ksm(ll a,ll b){ll ans=1;while(b){if(b&1) ans=ans*a%mod;a=a*a%mod;b>>=1;}return ans%mod;
}
int main(){int T;scanf("%d",&T);while(T--){int n;scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%lld",&a[i]);}if(n<=2) printf("1\n");else{ll ans=1;for(int i=1;i<=n;i++){int j=i;while(j<n&&a[j+1]==a[i]) j++;//连续的相等的元素个数if(i>=2&&j<=n-1&&((a[i-1]<a[i]&&a[i]<a[j+1])||(a[i-1]>a[i]&&a[i]>a[j+1]))){ans=ans*ksm(2,j-i+1)%mod;}else{ans=ans*(ksm(2,j-i+1)-1)%mod;}i=j;}printf("%lld\n",ans);}}return 0;
}

D-删除子序列(枚举优化)

给出一个长度为 n n n的字符串 S S S和一个长度为 m m m的不含重复字符的字符串 T T T,每次可以在 S S S中删除一个等于 T T T的子序列,最多可以删除多少次?

思路:
能够删除多少次取决于 T T T中的字母在 S S S中出现的位置。
假如 T T T中最后一个字母最后在 S S S中出现的位置为 i i i,那么 S S S中在 i i i之后的所有字母都不用予以考虑。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
const ll mod=1e9+7;
char s[maxn],t[maxn];
vector<int>vc[26];
int main(){int T;scanf("%d",&T);while(T--){int n,m;scanf("%d%d",&n,&m);scanf("%s%s",&s,&t);for(int i=0;i<26;i++) vc[i].clear();for(int i=0;i<n;i++){//标记s中各字母出现的位置 vc[s[i]-'a'].emplace_back(i);}int ans=0; while(1){int last=n;//s中能删去的字母的位置必须在last的前面 int flag=1;for(int i=m-1;i>=0;i--){while(vc[t[i]-'a'].size()&&vc[t[i]-'a'].back()>=last){//需要删除的字母的位置在last之后,则无需考虑,直接略过 vc[t[i]-'a'].pop_back();}if(vc[t[i]-'a'].empty()){//s中不存在该字母,直接结束 flag=0;break;}last=vc[t[i]-'a'].back();//更新last vc[t[i]-'a'].pop_back(); //该位置的该字母成功被删除 }if(!flag) break;ans++; }printf("%d\n",ans);}return 0;
}

E-骑士(贪心)

有 n n n位骑士,第 i i i位骑士的战斗力为 a i a_i ai​,防御力为 b i b_i bi​,生命值为 h i h_i hi​。
对于两位骑士 i , j ( i ≠ j ) i,j(i\neq j) i,j(i​=j),如果 a i − b j ≥ h j a_i-b_j\geq h_j ai​−bj​≥hj​,则第 i i i位骑士是可以秒杀第 j j j位骑士的,注意可能存在两位骑士能够相互秒杀(取决于谁先出手)。
在骑士大赛中, n n n位骑士需要两两进行战斗,由于被秒是件不光彩的事情,所以 n n n位骑士都要去商店购买生命药剂使得自己的生命值 h i h_i hi​提升,使得自己不会被除自己以外的任何一位骑士秒。
1支生命药剂可以提升骑士1点生命值,为满足所有骑士的需求,商店想知道自己至少需要准备多少支生命药剂。

思路:
要使得自己不被除自己以外的任何一位骑士秒,只要自己的生命值 h h h大于:
除自己外战斗力的最大值 − - −自己的防御力
即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
const ll mod=1e9+7;
ll a[maxn],b[maxn],h[maxn];
vector<ll>c;
int main(){int t;scanf("%d",&t);while(t--){ll n;scanf("%lld",&n);c.clear();for(int i=0;i<n;i++){scanf("%lld%lld%lld",&a[i],&b[i],&h[i]);c.push_back(a[i]);}sort(c.begin(),c.end());ll mmax=c[n-1];//战斗力最大值ll smax=c[n-2];//战斗力次大值ll ans=0;for(int i=0;i<n;i++){if(a[i]==mmax){if(smax-b[i]>=h[i]) ans+=smax-b[i]-h[i]+1;}else{if(mmax-b[i]>=h[i]) ans+=mmax-b[i]-h[i]+1;}}printf("%lld\n",ans);}return 0;
}

F- + − + - +−串(签到)

有一个仅包含 + + +和 − - −字符的字符串 S S S,可对其进行恰好 k k k次修改,每次修改选择一个整数 1 ≤ i ≤ ∣ S ∣ 1\leq i\leq |S| 1≤i≤∣S∣将 S i S_i Si​翻转,即如果 S i = + S_i=+ Si​=+,则将其修改为 − - −,如果 S i = − S_i=- Si​=−,则修改为 + + +。
同一个位置可以修改多次。
现有一整数 x x x,初始时 x = 0 x=0 x=0。
遍历上述字符串,对于 i = 1 , 2 , ⋯ , ∣ S ∣ i=1,2,\cdots ,|S| i=1,2,⋯,∣S∣,做以下操作:
如果 S i = + S_i=+ Si​=+,则让 x = x + 1 x=x+1 x=x+1。
如果 S i = − S_i=- Si​=−,则让 x = x − 1 x=x-1 x=x−1。
试求出对字符串进行修改之后,能够得到的 ∣ x ∣ |x| ∣x∣最小是多少?

思路:
最理想情况:字符串中 + + +和 − - −的数量相等,则 ∣ x ∣ = 0 |x|=0 ∣x∣=0。
所以要想使 ∣ x ∣ |x| ∣x∣达到最小,在对字符串进行修改时,就要尽可能让 + + +和 − - −的数量接近甚至相等。

  • 如果可以通过 k k k次修改,将两者数量变为相等,则需进一步考虑是否还剩余有修改次数。

    • 如果剩余偶数次修改,则结果不变(同一位置修改偶数次,结果不变)
    • 如果剩余奇数次修改,则现修改偶数次(结果不变),最后再修改一次(对结果影响最小)
  • 如果不能使两者数量变为相等,则尽量缩小二者差距(少的加 k k k,多的减 k k k)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
const ll mod=1e9+7;
int main(){ll t;scanf("%lld",&t);while(t--){char s[maxn];scanf("%s",s);ll num1=0,num2=0;ll len=strlen(s);for(int i=0;i<len;i++){//数量统计if(s[i]=='+') num1++;else num2++;}ll k;scanf("%lld",&k);ll ans=0;ll m=max(num1,num2);ll n=min(num1,num2);ll tmp=m-n;//数量差ll a=tmp/2;//要使+和-数量相等需要进行的修改次数(数量多的那个减少a,数量少的那个增加a)if(k>=a){//先修改a次,两者数量相等,再依据剩余次数的奇偶进行修改。m-=a;n+=a;k-=a;k%=2;m-=k;n+=k;}else{m-=k;n+=k;k=0;}printf("%lld\n",abs(m-n)); }return 0;
}

H-寒冬信使2(博弈、SG)

有 n n n个格子排成一排,每个颜色为黑色或白色,第 i i i个格子的颜色为 c i c_i ci​,如果 c i = b , 则 第 c_i=b,则第 ci​=b,则第i 个 格 子 为 黑 丝 , 如 果 个格子为黑丝,如果 个格子为黑丝,如果c_i=w , 则 第 ,则第 ,则第i 个 格 子 颜 色 为 白 色 。 牛 牛 和 牛 妹 在 这 个格子颜色为白色。 牛牛和牛妹在这 个格子颜色为白色。牛牛和牛妹在这n$个格子上玩游戏,两个人轮流操作,牛妹先手,每次操作可以为以下类型之一:
1、选择两个整数 i , j ( 1 ≤ i < j ≤ n ) i,j(1\leq i<j\leq n) i,j(1≤i<j≤n)满足第 j j j个石子为白色,同时翻转第 i i i个格子和第 j j j个格子的颜色。
2、翻转第1个格子的颜色(第1个格子的颜色为白色时才能进行此操作)。
不能操作的人输,牛牛和牛妹都非常聪明,牛牛和牛妹谁会赢?

思路:
博弈、SG函数

全黑为必败态

I-A+B问题(模拟加法)

给出两个非负 k k k进制整数 A , B A,B A,B,求 A + B A+B A+B。
2 ≤ k ≤ 10 2\leq k\leq 10 2≤k≤10
1 ≤ ∣ A ∣ ≤ 200000 1\leq |A|\leq 200000 1≤∣A∣≤200000
1 ≤ ∣ B ∣ ≤ 200000 1\leq |B|\leq 200000 1≤∣B∣≤200000
|A|,|B|表示A和B的长度。

思路:
k进制加法模拟
高精度
不要用string,会超时!

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
const ll mod=1e9+7;
int k;
void add(char a[],char b[]){vector<int>ans;int p=strlen(a)-1;int q=strlen(b)-1;int cnt=0;//进位while(p>=0&&q>=0){int tmp=a[p]-'0'+(b[q]-'0')+cnt;cnt=tmp/k;ans.push_back(tmp%k);p--;q--;}while(p>=0){int tmp=a[p]-'0'+cnt;cnt=tmp/k;ans.push_back(tmp%k);p--;}while(q>=0){int tmp=b[q]-'0'+cnt;cnt=tmp/k;ans.push_back(tmp%k);q--;}if(cnt!=0) ans.push_back(cnt);for(int i=ans.size()-1;i>=0;i--){printf("%d",ans[i]);}printf("\n");
}
int main(){scanf("%d",&k);char a[maxn],b[maxn];scanf("%s",a);scanf("%s",b);add(a,b);return 0;
}

J-牛妹的数学难题(组合数)

给出长度为 n n n的数组 a 1 , a 2 , . . . , a n ( 0 ≤ a i ≤ 2 ) a_1,a_2,...,a_n(0\leq a_i\leq 2) a1​,a2​,...,an​(0≤ai​≤2),求
∑ i 1 = 1 n ∑ i 2 = i 1 + 1 n . . . ∑ i k = i k − 1 + 1 n a i 1 a i 2 ⋯ a i k \sum_{i_1=1}^n\sum_{i_2=i_1+1}^n...\sum_{i_k=i_{k-1}+1}^na_{i_1}a_{i_2}\cdots a_{i_k} ∑i1​=1n​∑i2​=i1​+1n​...∑ik​=ik−1​+1n​ai1​​ai2​​⋯aik​​
输出答案除以998244353的余数。

思路:
求组合数
将上式展开,每一项都是 k k k个元素相乘,因此存在0的项的值一定为0,不必考虑。
需考虑只存在1和2的项。
假设某一项有 x x x个1, k − x k-x k−x个2,则该项的值为 2 k − x 2^{k-x} 2k−x
若给出数组中有 a a a个1, b b b个2,则某项出现 x x x个1, k − x k-x k−x个2的组合方式共有 C a x C b k − x C_a^xC_b^{k-x} Cax​Cbk−x​
综上:该项的值即为 C a x C b k − x 2 k − x C_a^xC_b^{k-x}2^{k-x} Cax​Cbk−x​2k−x
所有项的总和即为:
∑ x = 0 k C a x C b k − x 2 k − x \sum_{x=0}^kC_a^xC_b^{k-x}2^{k-x} ∑x=0k​Cax​Cbk−x​2k−x

#include <bits/stdc++.h>
using namespace std;
const int N=1e7+5;
const int mod=998244353;
typedef long long ll;
int inv[N];
int fact[N], infact[N],p[N];
void init(){fact[0] = infact[0] = inv[0] = inv[1] = 1;for (int i = 2; i <= N; ++ i)inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;for (int i = 1; i <= N; ++ i) {fact[i] = 1ll * fact[i - 1] * i % mod;infact[i] = 1ll * infact[i - 1] * inv[i] % mod;}
//  p[0]=1;
//  for(int i=1;i<=N;i++) p[i]=p[i-1]*2%mod;
}
ll ksm(ll a,ll b){ll ans=1;while(b){if(b&1) ans=ans*a%mod;a=a*a%mod;b>>=1;}return ans%mod;
}
int C(int n,int m){if(n<m) return 0;return 1ll*fact[n]*infact[m]%mod*infact[n-m]%mod;
}
int main(){int n,k;scanf("%d%d",&n,&k);init(); int a=0,b=0;for(int i=0;i<n;i++){int x;scanf("%d",&x);if(x==1) a++;if(x==2) b++;} ll ans=0;for(int i=0;i<=k;i++){ll tmp=1ll*C(a,i)*C(b,k-i)%mod*ksm(2,k-i)%mod;ll tmp=1ll*C(a,i)*C(b,k-i)%mod*p[k-i]%mod;ans=(ans+tmp)%mod;}printf("%lld\n",ans);
}

2022牛客寒假算法基础集训营相关推荐

  1. 2022牛客寒假算法基础集训营6 签到题5题(附基础集训营4-6签到题总结)

    1.I-A+B问题 模拟,类似于高精度,竖式运算 #include<bits/stdc++.h> using namespace std; typedef long long LL; in ...

  2. 2022牛客寒假算法基础集训营3 签到题7题(附基础集训营1-3签到题总结)

    1.A-智乃的Hello XXXX 签到 #include<bits/stdc++.h> using namespace std; int main(){cout<<" ...

  3. 2022牛客寒假算法基础集训营1 L E J H F C D A I (9/13)

    2022牛客寒假算法基础集训营1 L E J H F C D A I (9/13) L 牛牛学走路 思路: 模拟,用一个 M A X MAX MAX存储. 参考代码: #include<bits ...

  4. 2022牛客寒假算法基础集训营1 ——H 牛牛看云

    链接:登录-专业IT笔试面试备考平台_牛客网 来源:牛客网 就像罗夏墨迹测试一样,同一片形状的云在不同人的眼中会看起来像各种各样不同的东西. 例如,现在天上飘过了一片长条状的云彩,hina说这片云长得 ...

  5. 2022牛客寒假算法基础集训营4 签到题7题

    1.E-真假签到题 不难发现,或者随便枚举一下,可以得到f(n)=n的结论 #include<bits/stdc++.h> typedef long long LL; using name ...

  6. 2022牛客寒假算法基础集训营2 签到题7题

    1.C 小沙的杀球 如果你能够杀球但不杀球,虽然回复了体力,但你后续可能会没有机会继续杀球,并且杀球次数相同,那么回复的体力是相同的,所以在同等条件下,我们应该尽可能多的杀球. 不开long long ...

  7. 2022牛客寒假算法基础集训营1 签到题7题

    1.L.牛牛学走路 恭喜你 签到成功 #include<bits/stdc++.h> using namespace std; int main(){int T; cin>>T ...

  8. [nk] 2022牛客寒假算法基础集训营1 补题|题解

    目录 前言 L.牛牛学走路 MyCode OtherCode J.小朋友做游戏 MyCode A.九小时九个人九扇门 MyCode F. 中位数切分 MyCode 前言 根据难度系数补题,大概会补 A ...

  9. 2022牛客寒假算法基础集训营5 E.复苏小孩 详解

    题目描述 获得鬼手后,九峰成功让鬼眼,无头鬼影,鬼手三者形成了奇妙的平衡,短时间内不用担心厉鬼复苏,且可以使用厉鬼的力量. 但想要让三者保持平衡,必须按照一定的规律轮流使用各部分的力量,于是九峰向神秘 ...

最新文章

  1. 用java写一个简单的区块链(下)
  2. SM12表条目冻结说明
  3. Install ruby on rails in Ubuntu 12.04
  4. 【送书活动】C# 程序员的自我修养
  5. 竞赛图 计算机网络 应用题,我校学子获2020年“中国高校计算机大赛-网络技术挑战赛”全国总决赛一等奖(图)...
  6. 重磅快讯:CCF发布最新版推荐中文科技期刊目录
  7. MapReduce的map流程
  8. 如何调试Python extension
  9. Hacker Rank 上的 Even Tree 小议
  10. ifix与mysql_基于ODBC技术实现iFix组态软件与关系数据库通讯接口
  11. 威斯康星麦迪逊计算机专业排名,威斯康星大学麦迪逊分校计算机工程学科排名...
  12. 常见bat命令(二)
  13. 【NYOJ】[845]无主之地1
  14. kafka生产者的发送消息的流程以及代码案例
  15. 每周新品|鉴黄API上线 云市场2017最值得期待的产品盘点
  16. .NET/C#大型项目研发必备(7)--DataAccess数据库访问技术
  17. 一款国内开发的原型设计软件,非常棒!
  18. java抄表电表,国内外常见抄表的电表有哪几种形式
  19. QQ plot 的解读
  20. 【Modelsim常见问题】Can‘t launch the ModelSim-Altera software

热门文章

  1. http1.0,http.1.1,http2.0区别
  2. 喜马拉雅前端面经整理
  3. 有些新闻,公众可以遗忘,但程序员不能遗忘!
  4. 如何下载Amazon页面产品视频
  5. html菜单三条横线,Word上怎么制作就三条横线的表格?
  6. 计算机行业未来的规划模板,大学生计算机职业生涯规划书模板
  7. Unix、Linux、Windows操作系统的区别
  8. springBoot上传文件大小设置
  9. 被迫营业的网店订单管理系统!表弟的毕设!这也太简单了!
  10. JSONUtils工具类(基于alibaba fastjson)