2020牛客寒假算法基础训练营1
2020牛客寒假算法基础训练营1
A.honoka和格点三角形(计数)
题意:给定n*m个格点构造出的矩阵,问最多可以构造出多少个面积为1的三角形
题解:直接计数,讨论所有情况即可,注意乘法取模
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cmath>
#include<vector>
using namespace std;
using ll = long long;
const ll N = 1e6;
const double PI = acos(-1.0);
#define Test ll tesnum;tesnum = read();while(tesnum--)
ll read();
ll mod = 1e9+7;
int main()
{
ll n,m;
cin>>n>>m;
ll s=(n-2)*(m-1)*4%mod+(n-1)*(m-2)*4%mod;
s=(s+2*(n-1)*(m-2)%mod*(m-2)%mod+2*(m-1)*(n-2)%mod*(n-2)%mod)%mod;
s=(s+2*(n-2)*(m-1)%mod*(m-2)%mod+2*(m-2)*(n-1)%mod*(n-2)%mod)%mod;
cout<<s<<endl;
return "BT7274", NULL;
}
inline ll read() {
ll hcy = 0, dia = 1;char boluo = getchar();
while (!isdigit(boluo)) {if (boluo == '-')dia = -1;boluo = getchar();}
while (isdigit(boluo)) {hcy = hcy * 10 + boluo - '0';boluo = getchar();}
return hcy * dia;
}
B.kotori和bangdream(期望)
题意:每次有x%的概率获得a分,剩下的概率获得b分,一共进行n次,求得分期望
题解:结果是ans = (x%a+(1-(x%))b)*n
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cmath>
#include<vector>
#include<iomanip>
using namespace std;
using ll = long long;
const ll N = 1e6;
const double PI = acos(-1.0);
#define Test ll tesnum;tesnum = read();while(tesnum--)
ll read();
int main()
{
double n,x,a,b;
cin>>n>>x>>a>>b;
x/=100;
double ans = n*(x*a+(1-x)*b);
cout<<fixed<<setprecision(2);
cout<<ans<<endl;
return "BT7274", NULL;
}
inline ll read() {
ll hcy = 0, dia = 1;char boluo = getchar();
while (!isdigit(boluo)) {if (boluo == '-')dia = -1;boluo = getchar();}
while (isdigit(boluo)) {hcy = hcy * 10 + boluo - '0';boluo = getchar();}
return hcy * dia;
}
C.umi和弓道(双指针)
题意:给定一个起始坐标和一群坐标,这些坐标一定不在坐标轴上,现在需要在x轴或y轴设立一个挡板,这个挡板会遮蔽其他坐标和起始坐标的连线,要求遮蔽后最多只有k个点可以连通起始坐标,求这个挡板最短的长度
题解:分两种情况讨论,挡板在x轴上或在y轴上,将其他坐标与起始坐标的连线在x轴和y轴的交点分别记录下来,维护一个挡板长度的最小值,用双指针扫描
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cmath>
#include<vector>
#include<iomanip>
using namespace std;
using ll = long long;
const ll N = 1e6;
const double PI = acos(-1.0);
#define Test ll tesnum;tesnum = read();while(tesnum--)
ll read();
int main()
{
double x0,y0;
int n,k;
cin>>x0>>y0>>n>>k;
k = n - k;
vector<double> x,y;
for(int i = 0; i < n; i++){
double x1,y1;
cin>>x1>>y1;
if(x1*x0<0){
y.push_back(y0-x0*(y1-y0)/(x1-x0));
}
if(y1*y0<0){
x.push_back(x0-y0*(x1-x0)/(y1-y0));
}
}
sort(x.begin(),x.end());
sort(y.begin(),y.end());
double ans = 1e18;
if(x.size()>=k){
int l = 0,r = k-1;
while(r<x.size()){
ans = min(ans,x[r]-x[l]);
r++;l++;
}
}
if(y.size()>=k){
int l = 0,r = k-1;
while(r<y.size()){
ans = min(ans,y[r]-y[l]);
r++;l++;
}
}
cout<<fixed<<setprecision(8);
if(ans==1e18){
cout<<-1<<endl;
}else
cout<<ans<<endl;
return "BT7274", NULL;
}
inline ll read() {
ll hcy = 0, dia = 1;char boluo = getchar();
while (!isdigit(boluo)) {if (boluo == '-')dia = -1;boluo = getchar();}
while (isdigit(boluo)) {hcy = hcy * 10 + boluo - '0';boluo = getchar();}
return hcy * dia;
}
D.hanayo和米饭(暴力)
题意:1,2,3...n这n个数会给你n-1个,让你把那个没给你的数输出
题解:直接暴力
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cmath>
#include<vector>
using namespace std;
using ll = long long;
const ll N = 1e6;
const double PI = acos(-1.0);
#define Test ll tesnum;tesnum = read();while(tesnum--)
ll read();
bool vis[100005];
int main()
{
int n,x;
cin>>n;
for(int i = 0; i < n-1; i++)
{
cin>>x;
vis[x] = true;
}
for(int i = 1; i <= n; i++)
{
if(!vis[i]){
cout<<i<<endl;
break;
}
}
return "BT7274", NULL;
}
inline ll read() {
ll hcy = 0, dia = 1;char boluo = getchar();
while (!isdigit(boluo)) {if (boluo == '-')dia = -1;boluo = getchar();}
while (isdigit(boluo)) {hcy = hcy * 10 + boluo - '0';boluo = getchar();}
return hcy * dia;
}
E.rin和快速迭代(数论)
题意:给定一个数x,函数f(x)的值为x的因子个数,若当前的f(x)不为2,则继续进行f(f(x)),直到f(x)为2,求此时的计算次数
题解:本质上就是利用O(\(\sqrt{n}\))的积性函数\(\tau\)(n)暴力枚举出结果判断即可
\(\tau\)(n)函数表示的是正整数n的所有正因子个数,设n的质因子分解为n=\(p_1^{a_1}\)*\(p_2^{a_2}\)......\(p_s^{a_s}\),那么可以得出
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cmath>
#include<vector>
using namespace std;
using ll = long long;
const ll N = 1e6;
const double PI = acos(-1.0);
#define Test ll tesnum;tesnum = read();while(tesnum--)
ll read();
ll count(ll n)
{
ll s = 1;
for(ll i = 2;i*i<=n;i++){
if(n%i==0){
ll a = 0;
do{
n/=i;
a++;
}while(n%i==0);
s = s*(a+1);
}
}
if(n>1)s*=2;
return s;
}
int main()
{
ll n;
ll ans = 0;
cin>>n;
while(1){
ans++;
if(count(n)==2)break;
n = count(n);
}
cout<<ans<<endl;
return "BT7274", NULL;
}
inline ll read() {
ll hcy = 0, dia = 1;char boluo = getchar();
while (!isdigit(boluo)) {if (boluo == '-')dia = -1;boluo = getchar();}
while (isdigit(boluo)) {hcy = hcy * 10 + boluo - '0';boluo = getchar();}
return hcy * dia;
}
F.maki和tree(并查集)
题意:给定一个n个点n-1条边的树,每个点为“W”白色或“B”黑色,问可以构成多少条两点之间只有一个黑色点的简单路径。、
题解:经过一个黑点的简单路径有两种,一种是路径两端都是白点,一种是有一端是黑点,我们统计每个白色连通块的权值,然后通过统计每个黑点相邻白点的权值和求出第二种的路径,对于第一种的路径,假设1个黑点有k个相邻白点,第一种路径就是从第1个白点开始,剩余的白点权值总和与当前白点的乘积,即
\(\sum_{i=1}^{k}\sum_{j=i+1}^kf(i)*f(j)\)
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cmath>
#include<vector>
using namespace std;
using ll = long long;
const ll N = 111111;
const double PI = acos(-1.0);
#define Test ll tesnum;tesnum = read();while(tesnum--)
ll read();
int f[N];
ll knum[N];
ll st[N];
vector<int> g[N];
ll n;
string s;
int find(int x)
{
if(f[x]==x)return x;
else
return find(f[x]);
}
void uni(int a,int b)
{
int p = find(a);int q = find(b);
if(p!=q){
if(knum[p]>knum[q]){
f[q] = p;
knum[p]+=knum[q]+1;
}else
{
f[p] = q;
knum[q]+=knum[p]+1;
}
}
}
ll get(vector<int>temp)
{
ll len = temp.size();
ll res = 0;
if(len==0)return 0;
vector<int> pre(len+10,0);
pre[0] = temp.at(0);
for(int i = 0; i < len; i++)//统计出以黑点为一端的种数
res+=temp[i];
for(int i = 1; i < len; i++)//前缀和维护每个点的权值合集
pre[i] += pre[i-1]+temp[i];
for(int i = 1; i < len; i++)//计算
res+=temp[i]*pre[i-1];
return res;
}
int main()
{
cin>>n>>s;
for(int i = 1;i <= n; i++)f[i] = i;
for(int i = 1;i < n; i++){
int x,y;
cin>>x>>y;
g[x].push_back(y);
g[y].push_back(x);
if(s[x-1]=='W'&&s[y-1]=='W')uni(x,y);
}
ll ans = 0;
for(int i = 1; i <= n; i++)st[i] = knum[find(i)]+1;
for(int i = 1; i <= n; i++)
{
if(s[i-1]=='B'){
vector<int> temp;
for(int j = 0; j < g[i].size(); j++){
if(s[g[i][j]-1]=='W')temp.push_back(st[g[i][j]]);
}
ans+=get(temp);
}
}
cout<<ans<<endl;
return "BT7274", NULL;
}
inline ll read() {
ll hcy = 0, dia = 1;char boluo = getchar();
while (!isdigit(boluo)) {if (boluo == '-')dia = -1;boluo = getchar();}
while (isdigit(boluo)) {hcy = hcy * 10 + boluo - '0';boluo = getchar();}
return hcy * dia;
}
G.eli和字符串(字符串,双指针)
题意:给定一个字符串,要求截取一个最短的子串,这个子串至少包含k个相同的字符
题解:利用map存放每一个字符在字符串中的下标,当前字符数量满足k时调出区间长度维护最小值即可
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cmath>
#include<vector>
using namespace std;
using ll = long long;
const ll N = 1e6;
const double PI = acos(-1.0);
#define Test ll tesnum;tesnum = read();while(tesnum--)
ll read();
int main()
{
int k,n;
cin>>n>>k;
map<char,vector<int>> mp;
string s;
int ans = 2000000;
cin>>s;
for(int i = 0; i < s.size(); i++)
{
mp[s[i]].push_back(i);
if(mp[s[i]].size()>=k){
int l = mp[s[i]][mp[s[i]].size()-k];
int len = i-l+1;
ans = min(ans,len);
}
}
if(ans==2000000)
cout<<-1<<endl;
else
cout<<ans<<endl;
return "BT7274", NULL;
}
inline ll read() {
ll hcy = 0, dia = 1;char boluo = getchar();
while (!isdigit(boluo)) {if (boluo == '-')dia = -1;boluo = getchar();}
while (isdigit(boluo)) {hcy = hcy * 10 + boluo - '0';boluo = getchar();}
return hcy * dia;
}
H.nozomi和字符串 (字符串,尺取法)
题意:给定一个01串,最多可以修改k次,仅限将0改为1,1改为0,问这个01串中最长连续子串是多长
题解:尺取维护k次修改区间
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cmath>
#include<vector>
using namespace std;
using ll = long long;
const ll N = 1e6;
const double PI = acos(-1.0);
#define Test ll tesnum;tesnum = read();while(tesnum--)
ll read();
string s;
int n,k;
int deal(char x)
{
int L=0,R=0,change=0,ans=1;
for (int i = 0; i < n; i++)
{
if(s[i]==x)
{
if(change<k)
{
change++;
R++;
}
else
{
while(L<=R&&s[L]!=x)L++;
L++;
R++;
}
}
else R++;
ans = max(ans,R-L);
}
return ans;
}
int main()
{
cin>>n>>k>>s;
cout<<max(deal('0'),deal('1'))<<endl;
return "BT7274", NULL;
}
inline ll read() {
ll hcy = 0, dia = 1;char boluo = getchar();
while (!isdigit(boluo)) {if (boluo == '-')dia = -1;boluo = getchar();}
while (isdigit(boluo)) {hcy = hcy * 10 + boluo - '0';boluo = getchar();}
return hcy * dia;
}
I.nico和niconiconi(线性DP)
题意:给定长度为n的字符串,子串nico权值为a,子串niconi权值为b,子串niconiconi权值为c,求这个字符串的最大权值,子串不可重复使用
题解:dp即可,计dp[i]表示前i个字符的最大权值,那么有转移方程
if(substring(i-3,i))==nico,dp[i] = max(dp[i-4]+a.dp[i])
if(substring(i-5,i))==nico,dp[i] = max(dp[i-6]+b.dp[i])
if(substring(i-9,i))==nico,dp[i] = max(dp[i-10]+c.dp[i])
最后输出dp[n]即可
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cmath>
#include<vector>
using namespace std;
using ll = long long;
const ll N = 1e6;
const double PI = acos(-1.0);
#define Test ll tesnum;tesnum = read();while(tesnum--)
ll read();
int main()
{
vector<ll> dp(300005,0);
int n,a,b,c;
string s;
cin>>n>>a>>b>>c;
cin>>s;
for(int i = 0; i < s.size(); i++){
if(i>0)
dp[i] = dp[i-1];
if(i>=3&&s.substr(i-3,4)=="nico"){
dp[i] = max(dp[i-3]+a,dp[i]);
}
if(i>=5&&s.substr(i-5,6)=="niconi"){
dp[i] = max(dp[i-5]+b,dp[i]);
}
if(i>=9&&s.substr(i-9,10)=="niconiconi"){
dp[i] = max(dp[i-9]+c,dp[i]);
}
}
cout<<dp[n-1]<<endl;
return "BT7274", NULL;
}
inline ll read() {
ll hcy = 0, dia = 1;char boluo = getchar();
while (!isdigit(boluo)) {if (boluo == '-')dia = -1;boluo = getchar();}
while (isdigit(boluo)) {hcy = hcy * 10 + boluo - '0';boluo = getchar();}
return hcy * dia;
}
J.u's的影响力(矩阵快速幂,费马小定理)
题意:给定n,x,y,a,b,设f(1) = x,f(2) = y,之后为f(i) = f(i-1) * f(i-2) *\(a^b\),让你求f(n)的值,结果%1e9+7。
题解:通过观察你会发现表达式由x,y,a三个因子组成,且x和y的幂数都构成斐波那契数列,而a的幂数则是斐波纳契数列的变种,这时候很容易就想到利用矩阵快速幂去解决这个问题。利用费马小定理进行降幂处理,因为%的1e9+7是一个质数,所以对于不等于1e9+7的数字a来说,\(a^{1e9+6} \equiv (1 mod 1e9+7)\),这样可以对x,y,a的系数进行降幂,而它们的系数则有矩阵快速幂递推公式求出。复杂度为O(logn)
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cctype>
#include<string>
#include<stack>
#include<queue>
#include<set>
#include<map>
#include<cmath>
#include<vector>
using namespace std;
using ll = long long;
const ll N = 1e6;
const double PI = acos(-1.0);
#define Test ll tesnum;tesnum = read();while(tesnum--)
ll read();
const int MOD = 1e9+7;
struct Mx{
ll mx[2][2];
void init(){
for(int i = 0; i < 2; i++){
for(int j = 0; j < 2; j++){
mx[i][j] = (i==j?1:0);
}
}
}
void clear(){
for(int i = 0; i < 2; i++){
for(int j = 0; j < 2; j++){
mx[i][j] = 0;
}
}
}
};
Mx Mxmulti(Mx a, Mx b,int mod)
{
Mx res;
res.clear();
for(int i = 0; i < 2; i++){
for(int j = 0; j < 2; j++){
for(int k = 0; k < 2; k++){
res.mx[i][j]+=a.mx[i][k]*b.mx[k][j]%mod;
res.mx[i][j]%=mod;
}
}
}
return res;
}
Mx Mxpow(Mx a,ll b,int mod)
{
Mx ans;ans.init();
while(b > 0){
if(b&1)
ans = Mxmulti(ans,a,mod);
b>>=1;
a = Mxmulti(a,a,mod);
}
return ans;
}
ll quick_mod(ll a, ll b, ll mod)
{
if(b==0)return 1;
ll ans = 1;
a%=mod;
while(b>0)
{
if(b&1)
ans = (ans*a)%mod;
b>>=1;
a = (a*a)%mod;
}
return ans%mod;
}
int main()
{
ll n,a,b,x,y;
cin>>n>>x>>y>>a>>b;
if(n==1){
cout<<x%MOD<<endl;
}else
if(n==2){
cout<<y%MOD<<endl;
}else
if(x%MOD==0||y%MOD==0||a%MOD==0){
cout<<0<<endl;
}else{
Mx A;
A.clear();
A.mx[0][0] = A.mx[0][1] = A.mx[1][0] = 1;
A = Mxpow(A,n-2,MOD-1);
ll f1 = A.mx[0][0];
ll f2 = A.mx[0][1];
ll ans = 1;
ans = ans*quick_mod(x,f2,MOD)%MOD;
ans = ans*quick_mod(y,f1,MOD)%MOD;
ans = ans*quick_mod(quick_mod(a,b,MOD),f1+f2-1,MOD)%MOD;
cout<<ans<<endl;
}
return "BT7274", NULL;
}
inline ll read() {
ll hcy = 0, dia = 1;char boluo = getchar();
while (!isdigit(boluo)) {if (boluo == '-')dia = -1;boluo = getchar();}
while (isdigit(boluo)) {hcy = hcy * 10 + boluo - '0';boluo = getchar();}
return hcy * dia;
}