比赛链接:https://www.nowcoder.com/acm/contest/117#question
D | 雷电爆裂之力 |
题意:题意:有三个长度分别为 n, m, k 的元素值严格递增的整数数组a, b, c。求 min(abs(ai − bj) + abs(bj − cp)) + 3 的值,其中1 ≤ i ≤ n, 1 ≤ j ≤ m, 1 ≤ p ≤ k。
解析:一个比较简单的思路是枚举 bj,通过双指针的方法,找到距离 bj 最近的 ai 和 cp,更新答案。
代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=100010; int n,m,k; ll a[N],b[N],c[N]; int main() { int T; scanf("%d",&T); while(T--) { scanf("%d%d%d",&n,&m,&k); for(int i=0;i<n;i++) scanf("%lld",&a[i]); for(int i=0;i<m;i++) scanf("%lld",&b[i]); for(int i=0;i<k;i++) scanf("%lld",&c[i]); ll ans=1e12; int pos1=0,pos2=0; for(int i=0;i<m;i++) { ll dis1=1e12,dis2=1e12; while(pos1<n&&a[pos1]<b[i]) pos1++; if(pos1>0) dis1=min(dis1,b[i]-a[pos1-1]); if(pos1<n) dis1=min(dis1,a[pos1]-b[i]); while(pos2<k&&c[pos2]<b[i]) pos2++; if(pos2>0) dis2=min(dis2,b[i]-c[pos2-1]); if(pos2<k) dis2=min(dis2,c[pos2]-b[i]); ans=min(ans,dis1+dis2); } printf("%lld\n",ans+3); } return 0; }
E | 可以来拯救吗 |
题意:给出长度为n的序列:a1,a2...an,求所有长为k的子序列的和的平方的异或和。
解析:题目有如下说明
保证,也就是说,需要考虑的子序列不超过100000个。
那么如果n比较大的话,k要不就比较小,要不就接近n,所以可以用dfs来做,对于k<=n/2时,直接深搜长度为k的子序列,对于k>n/2时,深搜长度为n-k的子序列,然后用序列总和sum减之,来得到真正想要的长度为k的和。代码写的繁琐,可以在简化一下
代码:
#pragma comment(linker,"/STACK:102400000,102400000") #include <bits/stdc++.h> using namespace std; typedef long long ll; #define M 100005 ll n,k,a[M]; ll ans,sum; void dfs1(ll i,ll len,ll res) { if(len>k) return; for(ll j=i+1;j<=n;j++) //for(int j=i+1;j<=n;j++) { res+=a[j]; if(len==k) { //cout<<"res="<<res*res<<endl; ans=ans^(res*res); }else{ dfs1(j,len+1,res); } res-=a[j]; } } void dfs2(ll i,ll len,ll res) { if(len>k) return; for(ll j=i+1;j<=n;j++) //for(int j=i+1;j<=n;j++) { res+=a[j]; if(len==k) { //cout<<"res="<<res*res<<endl; ll tmp=sum-res; ans=ans^(tmp*tmp); }else{ dfs2(j,len+1,res); } res-=a[j]; } } int main() { //cout<<(1^4^9^16)<<endl; int T; scanf("%d",&T); while(T--) { scanf("%lld%lld",&n,&k); sum=0; for(int i=1;i<=n;i++) { scanf("%lld",&a[i]); sum+=a[i]; } ans=0; if(k<=n/2) { if(k==1) { for(int i=1;i<=n;i++) { ans=ans^(a[i]*a[i]); } }else { for(int i=1;i<=n-k+1;i++) { dfs1(i,2,a[i]); } } } else { k=n-k; if(k==0) { ans=sum*sum; }else if(k==1) { for(int i=1;i<=n;i++) { ans=ans^((sum-a[i])*(sum-a[i])); } } else { for(int i=1;i<=n-k+1;i++) { dfs2(i,2,a[i]); } } } printf("%lld\n",ans); } return 0; }
扫描二维码关注公众号,回复:
934935 查看本文章
F | 汤圆防漏理论 |
解析:直接贪心。记每个点点权为其当前连接的所有边的边权和,每次取出最小的点,然后删掉。用 set 动态维护这个过程。时间复杂度 O(n log n)。
代码:
#include <bits/stdc++.h> using namespace std; typedef long long ll; #define M 100005 ll sum[M]; //每个节点的粘度值 bool vis[M];//对于已经删除的节点i,vis[i]=true vector< pair<ll,ll> >G[M];//G[i]是一个pair数组记录与i点相邻点的情况(相邻点的编号和两点间权值) set< pair<ll,ll> >st; //set中元素自动升序排,set中放的是每个点的粘度值总和及其编号 int main() { int T,n,m; ll s,t,v; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); for(int i=0;i<M;i++) { G[i].clear(); sum[i]=0; vis[i]=false; } st.clear(); while(m--) { scanf("%lld%lld%lld",&s,&t,&v); G[s].push_back( pair<ll,ll>(t, v) ); G[t].push_back( pair<ll,ll>(s, v) ); sum[s]+=v;sum[t]+=v; } for(int i=1;i<=n;i++) { st.insert(pair<ll,ll>(sum[i],i)); } ll ans=0; while(!st.empty()) //set集合中是还未删除的点 { set< pair<ll,ll> >::iterator it=st.begin();//it指向粘度值最小的点 pair<ll,ll> now=*it; st.erase(it); //删除此点 ans=max(ans,now.first); //在删除点的过程中出现的最大的粘度值就是答案 ll index=now.second; //index是此点的编号 vis[index]=true; for(int i=0;i<G[index].size();i++)//枚举index点的相邻节点,删除两者之间的边(对index的相邻节点来说就是删除与点index之间的粘度值) { pair<ll,ll> next=G[index][i];//next是相邻点的信息 if(vis[next.first]) continue; ll u=next.first; //u是相邻点的编号 it=st.lower_bound(pair<ll,ll>(sum[u],u));//在set集合中找到点u //更新点u的信息 st.erase(it); sum[u]-=next.second; st.insert(pair<ll,ll>(sum[u],u)); } } printf("%lld\n",ans); } return 0; }
G | 命名规范问题 |
题意:给一些字符串,将符合 (题中描述的) 驼峰命名法规范的变量名转换为下划线命名法。不符合的原样输出。
规范的规则如下:
1.每个变量名由至少2个单词拼接构成,且每个单词长度至少为2;
2.每个单词的首字母必须大写,其他位置必须小写(除了变量名的第一个单词允许全部小写外)。正则表达式代码:
#include<bits/stdc++.h> using namespace std; int main() { regex reg("\\b[A-Za-z][a-z]+([A-Z][a-z]+)+\\b"); regex cap("[A-Z]"); int T; cin>>T; while(T--) { string now; cin>>now; if(regex_match(now,reg)) { now=regex_replace(now,cap,"_$0"); transform(now.begin(),now.end(),now.begin(),::tolower); } if(now[0]=='_') now.erase(0,1); cout<<now<<endl; } }
AC代码:
#include <bits/stdc++.h> using namespace std; typedef long long ll; #define M 200005 char str[M]; int main() { int T; scanf("%d",&T); while(T--) { scanf("%s",str); int len=strlen(str); int flag=0; for(int i=0;i<len;i++) { if(str[i]>='A'&&str[i]<='Z'){flag++;} } if(flag==0||(flag==1&&str[0]>='A'&&str[0]<='Z')) { printf("%s\n",str); continue; } flag=1; int num=0; for(int i=0;i<len;i++) { if(str[i]>='A'&&str[i]<='Z') { if(i!=0&&num<2) {flag=0;break;} num=1; }else{ num++; } } if(num<2) {flag=0;} if(flag==0) { printf("%s\n",str); continue; } string ans=""; for(int i=0;i<len;i++) { if(str[i]>='A'&&str[i]<='Z') { if(i!=0) ans+="_"; ans+='a'+(str[i]-'A'); }else{ ans+=str[i]; } } cout<<ans<<endl; } return 0; }