网址: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\n",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\n",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\n",r1);
else printf("%lld\n",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\n",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:\n",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题思路: