暑期集训7/14

网址:https://vjudge.net/contest/170761#overview

A题思路:

B题思路:

C题思路:

题目要求:

求最长连续子串,在相等大小下,长度要求最长,再起点最小。

思路:

因为ans都是大于等于零的,所以我们就当加到sum小于零时,就把sum归零,起点变成当前的i,继续。

核心代码:

int mx=a[1],sum=mx;
        int len=0;
        int x=1;
        int be=1,en=1;
        for(int i=2;i<n;i++)
        {
        	if(sum<0)
        	{
        		sum=0;
        		x=i;
			}
			sum+=a[i];  
            if(sum>mx||(sum==mx&&i-x>en-be))    
            {  
 				mx=sum;
 				be=x;
 				en=i;
            } 
        }    

 D题思路:

E题思路:

F题思路:

G题思路:

题目要求:

  在Windows下我们可以通过cmd运行DOS的部分功能,其中CD是一条很有意思的命令,通过CD操作,我们可以改变当前目录。
  这里我们简化一下问题,假设只有一个根目录,CD操作也只有两种方式:
  1. CD 当前目录名...目标目录名 (中间可以包含若干目录,保证目标目录通过绝对路径可达)
  2. CD .. (返回当前目录的上级目录)
  现在给出当前目录和一个目标目录,请问最少需要几次CD操作才能将当前目录变成目标目录?

思路:预处理,每个节点的深度,然后求他们的最近祖先(判断条件顺序要正确,不然wa到你心态爆炸)

代码:

#include <stdio.h>
#include <iostream>
#include <string.h>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector> 
#include <map>
using namespace std;
const int maxn=100007;
int fa[maxn];
int f[maxn][30];
int depth[maxn];
vector<int> tree[maxn];
map<string,int> mp;
void init(int u,int p,int d)
{
    f[u][0]=p,depth[u]=d;
    for(int i=1;i<=20;i++)
        f[u][i]=f[f[u][i-1]][i-1];
    int sz=tree[u].size();
    for(int i=0;i<sz;i++)
    {
        int v=tree[u][i];
        if(v!=p) init(v,u,d+1);
    }
}
int lca(int a,int b)
{
    if(depth[a]<depth[b]) swap(a,b);
    for(int i=19,d=depth[a]-depth[b];i>=0;i--)
    {
        if(d>>i&1)
        {
            a=f[a][i];
        }
    }
    if(a==b) return a;
    for(int i=19;i>=0;--i)
        if(f[a][i]!=f[b][i])
            a=f[a][i],b=f[b][i];
    return f[a][0];
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,m;
        memset(fa,0,sizeof(fa));
        mp.clear();
        int cnt=1;
        scanf("%d %d",&n,&m);
        for(int i=1;i<=n+5;i++) tree[i].clear();
        string a,b;
        for(int i=0;i<n-1;i++)
        {
            cin>>a>>b;
            //cout<<a<<"  "<<b<<endl;
        //    int f=mp[b],s=mp[a];
            if(mp[a]==0) mp[a]=cnt++;
            if(mp[b]==0) mp[b]=cnt++;
            //cout<<mp[a]<<"  "<<mp[b]<<endl;
            tree[mp[b]].push_back(mp[a]);
            fa[mp[a]]=1;
        } 
        //getchar();
        int root;
        for(int i=1;i<cnt;i++) if(fa[i]==0) root=i;
        init(root,root,0);
        for(int i=0;i<m;i++)
        {
            cin>>a>>b;
            //cout<<a<<"ttt"<<b<<endl; 
            int a1=mp[a],b1=mp[b];
            int ans;
            int samefather=lca(a1,b1);
            //cout<<samefather<<endl;
            if(samefather==b1) 
            {
                ans=depth[a1]-depth[b1];
            }
            else if(samefather==a1) ans=1;
            else 
            {
                ans=depth[a1]-depth[samefather]+1;
            }
            printf("%d
",ans);
        }
    }
    return 0;
}

H题思路:

题目要求: 求最多去掉M块石头的最短距离的最大值,其实就是去掉M块,因为你每去掉一块就会,使最短距离变大或者不变,所以去掉M就是最优的,然后弄个函数int f(int d)表示最短距离为d时候去掉的石头数目,然后我们二分d(0到L),看看在满足f(d)小于等于M的情况下的,最大d。

核心代码:

int f(int d)
{
    ll now=0;
    int k=1;
    int t=m;
    while(t>=0&&k<=n+1)
    {
        if(now+d>a[k]) t--;//cout<<now<<"  "<<k<<endl;
        else now=a[k];
        k++;
        //if(k==n+1) break;
    }
    if(t>=0) return 1;
    else return 0;
}
if(n==m)
    {
        return 0*printf("%lld
",l);
    }
    ll l1=0,r1=l;
    ll d=l/2;
    while(l1+1<r1)
    {
    //cout<<d<<"  "<<f(d)<<endl;
        if(f(d)) l1=d;
        else  r1=d;
        d=(r1+l1)/2;
    }
    if(f(r1))
    printf("%lld
",r1);
    else printf("%lld
",l1);

 I题思路:

题目要求:在m的时间内,把n个钱分成小于等于m个的组,使最大值最小

思路:二分(maxi,sumi),求出符合情况的最小值

核心代码:

bool judge(ll mid)
{
    ll sum=0,cnt=1;
    for(int i=1;i<=n;i++)
    {
        if(sum+a[i]<=mid)  //当前i天之和<=mid时,把他们归并到一组
            sum+=a[i];
        else               //若 前i-1天之和 加上第i天的花费 大于mid
        {
            sum=a[i];  //则把前i-1天作为一组,第i天作为下一组的第一天
            cnt++;    //此时划分的组数+1
        }
    }
    if(cnt>m)
    return false;
    else return true;
}//判断当前金额是否符合
ll l=mx,r=sum;
        ll md=(mx+sum)/2;
        while(l<r)
        {
        //cout<<md<<endl;

            if(!judge(md)) l=md+1;
            else r=md-1;
            md=(l+r)/2;
        }
        printf("%lld
",l);
//二分,输出符合情况的最小值

 J题思路:

直接看代码:

#include <iostream>
#include <stdio.h>
#include <map>
#include <cmath>
#define ll long long
using namespace std;
const int maxn=100010;
int st[25][maxn];
int gcd(int a,int b)
{
	return b==0?a:gcd(b,a%b);
}
int a[maxn];
int lg[maxn];
int rgcd(int l,int r)
{
	int k = lg[r-l+1];
	return gcd(st[k][l],st[k][r-(1<<k)+1]); //真的是脑残,把gcd写成max,然后找一个上午才找出这个错误,你是弱智吗,??????? 
}
int main()
{
	int T;
	//cout<<gcd(4,12);
	scanf("%d",&T);
	int cnt=1;
	lg[1]=0;
	for(int i=2;i<maxn;i++) lg[i]=lg[i>>1]+1;
	while(T--)
	{
		int n,m;
		scanf("%d",&n);
		// st,预处理任意区间的gcd 
		for(int i=1;i<=n;i++) scanf("%d",a+i),st[0][i]=a[i];
		for(int j=1;j<20;j++)
		{
			for(int i=1;i+(1<<j)<=n+1;i++)
			{
				st[j][i]=gcd(st[j-1][i],st[j-1][i+(1<<(j-1))]);
			}
		}
		//核心:gcd性质,同一左端点,随着数列的变长 ,gcd递减(不是严格递减),所以他们可能出现(线1,点2,线3)这种的 线1gcd>点2gcd>线3gcd(在同一左端点的情况下),然后我们枚举左端点o(n) ::线表示有多个点 ,增加一个数字,gcd减少的话,至除以2 
		map<int,ll> mp;
		for(int i=1;i<=n;i++)
		{
			int gd=a[i];
			int j=i;
			while(j<=n)
			{
				int l=j,r=n;
				
				while(l+1<r)
				{
					int mid=(l+r)>>1;
					//cout<<l<<"   "<<r<<endl;
					if(rgcd(l,r)==gd) l=mid;
					else r=mid;
				} 
				mp[gd]+=l-j+1,j=l+1;
				//if(gd==9) cout<<j<<endl;
				gd=gcd(gd,a[j]);
			}
		}
		scanf("%d",&m);
		printf("Case #%d:
",cnt);
		for(int i=0;i<m;i++)
		{
			int l,r;
			scanf("%d %d",&l,&r);
			int ansgcd=rgcd(l,r);
			cout<<ansgcd<<" "<<mp[ansgcd]<<endl;
		}
		cnt++;
		mp.clear(); 
	}
	return 0;
}

K题思路:

题目要求:求联通快的面积,输出先按最大面积,相同就按照字典序,用struct保存这个联通块的字符和面积(多=多组数据记得要重置struct)

核心代码:

int dfs(char ans,int x,int y)
{
	s[x][y]='.';
	int cnt=1;
	for(int i=0;i<=3;i++)
	{
		int nx=x+dx[i];
		int ny=y+dy[i];
		if(nx>=0&&nx<n&&ny>=0&&ny<m)
		{
			if(s[nx][ny]==ans) 
			{
				cnt+=dfs(ans,nx,ny);
			}
		}
	}
	return cnt;
}
//计算面积
for(int i=1;i<c;i++) 
		{
			sa[i].m=0;
			sa[i].ch='.';
		}
//重置struct

 L题思路:

要求:求联通快的最大面积,K题的缩水版

M题思路:

题目要求判断一个大整数是否是11的倍数;

方法一:(直接模拟)

for(int i=0;i<len;i++)
        {
            ans=(s[i]-'0')+ans*10;
            ans%=11;
        }
        cout<<s;
        if(ans%11==0)

 方法二:

一个十进制的数可以变成xn*10^n+......+x0*1,把每位都减去11就是xn*(-1)^n+...+x0*(-1)^0,

那就变成看看奇数位和偶数位之茶能不能整除11.

for(int i=0;i<len;i+=2)
            a+=(s[i]-'0');
        for(int i=1;i<len;i+=2)
            b+=(s[i]-'0');
        cout<<s;
        int x=abs(b-a);
        if(x%11==0)

 N题思路:

题目要求:切一条直线,使得左右两边点的数目相同;

思路:因为数据很小,直接暴力枚举即可

for(i=-500;i<=500;i++)
        {
            for(j=-500;j<=500;j++)
            {
                n1=0,n2=0;
                if(i==0&&j==0) continue;
                for(int k=1;k<=2*n;k++)
                {
                    if((i*x[k]+j*y[k])>0) n1++;
                    if((i*x[k]+j*y[k])<0) n2++;
                    if((i*x[k]+j*y[k])==0) break;
                }
                if(n1==n2&&n1==n) break;
            }
            if(n1==n2&&n1==n) break;
        }
        cout<<i<<" "<<j<<endl;

 O题思路:

原文地址:https://www.cnblogs.com/chinacwj/p/7172061.html