2020牛客寒假算法基础集训营1
A . honoka和格点三角形
链接:https://ac.nowcoder.com/acm/contest/3002/A
honoka最近在研究三角形计数问题。
她认为,满足以下三个条件的三角形是“好三角形”。
1.三角形的三个顶点均为格点,即横坐标和纵坐标均为整数。
2.三角形的面积为 。
3.三角形至少有一条边和 轴或 轴平行。
honoka想知道,在平面中选取一个大小为 的矩形格点阵,可以找到多少个不同的“好三角形”?由于答案可能过大,请对 取模。
输入描述:
两个正整数和( 2\ ≤n,m≤10^9)(2 ≤n,m≤10 ^9)
输出描述:
面积为1的格点三角形的数量,对 10^9+7 取模的结果。
面积为二的三角形 只能是 底1高2 或者底2高1 分别以横纵坐标轴标轴方向为底 计算共多少情况 需要注意的是 直角三角形会有重复,我计算了底边横向的所有情况
底边纵向的删去所有直角三角形 以底边横向为例
加入底边长 1 那么在一条横线上 可以有(m-1)个底边每个底边朝一个方向可以有多少个顶点呢,是m个。只要到底边距离为2就可以了,已经有一条边平行坐标轴了完全符合条件。 而每条线的三角形都有上下两个方向所以(但边缘的两个都只有一个)所以 再乘(2*n-2) 然后该底边是2的情况 只需将(m-1)改为(m-2)就行了
(长度为m的边上只能有(m-2)个长度为2的子边)
计算底边纵向时 和刚才死的分析思路是一样 就是要减去直角三角形 减多少个呢
以每个底边为例 底边的两端点对应的顶点所构成的就是直角三角形了,每个底边减二就可以了 注意別爆 ll
具体看代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010,mod=1e9+7;
ll cnt,n,m;
int main()
{
while(cin>>n>>m)
{
ll ans=((((n-2)*m)%mod)*(2*m-2))%mod;
ans=(ans+((n-1)*m)%mod*(2*m-4)%mod)%mod ;
ans=(ans+((m-2)*(n-2))%mod*(2*n-2)%mod)%mod;
ans=(ans+((m-1)*(n-2))%mod*(2*n-4)%mod)%mod;
cout<<ans<<endl;
}
return 0;
}
B 题 求期望就不说了 C没做(应该是最难的一道了)
D 排序后就知道缺那个数了
E. 求x的因子个数 只用遍历到 sqrt(x)就行了 所以最多也就是1e6的级别 不会超时 所以一直迭代就好了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100010,mod=1e9+7;
int d[8][2]={1,2,2,1,2,-1,1,-2,-1,-2,-2,-1,-1,2,-2,1};
ll a,b,n,m;
int f(ll x)
{
int cnt=0;
for(ll i=1;i*i<=x;i++)
if(x%i==0)
{
if(i*i==x)
cnt++;
else cnt+=2;
}
return cnt;
}
int main()
{
cin>>n;
for(int i=1;i<100010;i++)
{
int x=f(n);
if(x==2)
{
cout<<i<<endl;
break;
}
else n=x;
}
}
F 求树上路径最多只经过一个黑点的数量 所以黑点可能出现在端点或者中间
如果出现在端点 那么路径上其他点只能是白色 所以路径的数量就是黑点周围的白点的数量(与其他黑色节点之间的所有白节点)
如果出想在中间 那么白色节点就要经过这个黑节点与其他白节点相连(起点与终点必须位于黑点的不同的连通分支中) 求每个连通分支的与其他黑点之间的白点数量
再把任意两个分支的数量相乘就可以了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200010,mod=1e9+7;
int n,h[N],e[N],ne[N],idx;
char s[N];
vector<int> ve;
void add(int a,int b)
{
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
ll ans,sum[N];
int dfs(int x,int pre)
{
int res=1;
for(int i=h[x];i!=-1;i=ne[i])
{
int y=e[i];
if(s[y]=='B'||y==pre) continue;//pre 父节点
if(s[x]=='B')
{
ve.push_back(dfs(y,x));//每个连通分支的数量分别存储
}
else res+=dfs(y,x);
}
return res;
}
int main()
{
memset(h,-1,sizeof h);
cin>>n;
cin>>s+1;
for(int i=1;i<n;i++)
{
int a,b;
cin>>a>>b;
add(a,b);
add(b,a);
}
for(int i=1;i<=n;i++)
if(s[i]=='B')
{
ve.clear();
ve.push_back(0);//千缀合的下标最好从1开始 所以预存一个0不影响结果
dfs(i,-1);
for(int j=1;j<ve.size();j++)//求前缀和 方便求任意两个数的乘积
sum[j]=sum[j-1]+ve[j];
for(int j=2;j<ve.size();j++)
ans+=ve[j]*sum[j-1];//黑点在中间
ans+=sum[ve.size()-1];//黑点在端点 所有分支的总合
}
cout<<ans;
}
G 求含k个相同字母的最小子串长度
这种求最值的问题很容易就会想到二分了,那怎么二分呢
首先肯定是需要二分长度了 那么怎么快速的判断是否合适呢
只要子串长度大于等于k个某字母的起始区间长度,那这个长度就是合适的
首先预处理字符串 因为只有26个字母,所以完全可以将每个字母出现的位置存下来
而且同一字母 位置的数组内是单增的 只需要遍历看 判断长度是否合适就行了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200010,mod=1e9+7;
int d[8][2]={1,2,2,1,2,-1,1,-2,-1,-2,-2,-1,-1,2,-2,1};
ll a,b,n,k;
char s[N];
vector<int> ve[30];
bool check(int x)
{
for(int i=0;i<26;i++)
{
if(ve[i].size()>=k)
for(int j=0;j+k-1<ve[i].size();j++)
if(ve[i][j+k-1]-ve[i][j]<=x)
return 1;
}
return 0;
}
int main()
{
cin>>n>>k;
cin>>s;
for(int i=0;i<n;i++)
ve[s[i]-'a'].push_back(i);
int l=0,r=n,mid;
while(l<r)
{
mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
if(l>=n) cout<<-1;
else cout<<l+1; 二分的是不满足这个性质的最大长度
}
H.这道题还是可以二分,也是二分长度
把一段子串变成一样字符的最小操作=min(0的数量,1的数量);
怎么求一段字串的0和1的数量 对整段字符串求前缀和 然后就可以求区间和了,一段子串的区间和就是 1的数量了(没有对0计数)0的数量就是区间长度-区间和
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200010,mod=1e9+7;
int d[8][2]={1,2,2,1,2,-1,1,-2,-1,-2,-2,-1,-1,2,-2,1};
ll a,b,n,k;
char s[N];
int sum[N];
bool check(int x)
{
for(int i=1;i+x-1<=n;i++)
{
int res=sum[i+x-1]-sum[i-1];
if(min(res,x-res)<=k)
return 1;
}
return 0;
}
int main()
{
cin>>n>>k;
cin>>s+1;
for(int i=1;i<=n;i++)
if(s[i]=='1') sum[i]=sum[i-1]+1;
else sum[i]=sum[i-1];
int l=0,r=n,mid;
while(l<r)
{
mid=(l+r+1)>>1;//不要忘了加一
if(check(mid)) l=mid;
else r=mid-1;
}
cout<<l;
}
I . 不同的单词有不同的分数,每个字母只能用一次,问最大分数是多少
这题非常适合dp,不管a,b,c的大小关系如何 每次dp去最大值就好,亏我想了那么种情况两个多小时也没做对,我太菜了。dp得好好复习复习了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=300010,mod=1e9+7;
string str;
ll a,b,c,n;
ll ans,dp[N];
int main()
{
str.push_back('0');
string s;
cin>>n>>a>>b>>c>>s;
str+=s;
for(int i=1;i<=n;i++)
{
dp[i]=dp[i-1];//先更新再取最值
if(i>=3&&str.substr(i-3,4)=="nico")
{
dp[i]=max(dp[i],dp[i-3]+a);
}
if(i>=5&&str.substr(i-5,6)=="niconi")
dp[i]=max(dp[i],dp[i-5]+b);
if(i>=9&&str.substr(i-9,10)=="niconiconi")
dp[i]=max(dp[i],dp[i-9]+c);
}
cout<<dp[n];
return 0;
}