zoukankan      html  css  js  c++  java
  • 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;	
    }
    
  • 相关阅读:
    跨浏览器的事件对象(EventUtil)
    原型 Object.create
    DC大牛的function扩展
    事件代理 proxy
    错误处理程序
    JQuery 插件
    关于ClownFish的问题
    C#拾遗
    Jquery技巧
    防盗链
  • 原文地址:https://www.cnblogs.com/neflibata/p/12871789.html
Copyright © 2011-2022 走看看