比赛链接: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;
}
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;
}