网址: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题思路: