A:https://ac.nowcoder.com/acm/contest/884/A
给出n个点,和相连的边,k个人,让k个人在最短时间内同一地点相遇。
一句话题解:考虑距离最远的两个关键点,设它们的距离为d,d/2上取整即为答案。
• 必要性:这两个人要碰面,必然要走至少d/2步。
• 充分性:我们取两人路径中和一头距离为d/2上取整的一个点,让所有人在这相聚。如
果有一个人在d/2时间内到不了,那么它和路径两头中与它远的那一头的距离大于d,与
最远的假设矛盾。
• 找到这样最远的一对点类似找树的直径。可以直接dp,也可以采用两遍bfs:从任意一个关
键点开始,找到离它最远的关键点x,再从x开始bfs,找到的新的最远点和x形成的就是直径。
两次bfs找到直径,向上取整除以二
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int n,k,a,b;
vector<int> v[maxn];
queue<int> q;
bool vis[maxn];
int d[maxn],mx,l,pos[maxn];
void bfs(int s)
{
mx=0;
memset(vis,0,sizeof(vis));
memset(d,0,sizeof(d));
q.push(s);
vis[s]=1;
while(!q.empty()){
int x=q.front();
q.pop();
for(int i=0;i<v[x].size();i++){
int y=v[x][i];
if(vis[y]) continue;
d[y]=d[x]+1;
if(pos[y]&&d[y]>mx){
mx=d[y];l=y;
}
q.push(y);
vis[y]=1;
}
}
}
int main()
{
scanf("%d%d",&n,&k);
memset(pos,0,sizeof(pos));
for(int i=0;i<n-1;i++){
scanf("%d%d",&a,&b);
v[a].push_back(b);
v[b].push_back(a);
}
for(int i=0;i<k;i++){
scanf("%d",&a);
pos[a]=1;
}
bfs(a);
bfs(l);
printf("%d\n",(mx+1)/2);
return 0;
}
C:https://ac.nowcoder.com/acm/contest/884/C
很经典的题 max{min(al…r)×sum(bl…r)} 。
找到a数列的每个值是最小值的区间 l,r,用单调栈。
维护b数列前缀和的最大值和最小值,sum(b[l..r])=s[r]-s[l-1]。
分类讨论a[i]为正还是负,查询 i..r-1最大的s 和 l..i-1最小的s,相减即可
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=3e6+5;
const ll inf=1e18;
ll n,a[maxn],b[maxn],s[maxn],l[maxn],r[maxn];
stack<ll> sl,sr;
struct tree
{
struct node
{
ll l,r,mi,mx;
}t[maxn*4+5];
ll x,y;
void build(ll l,ll r,ll k)
{
t[k].l=l;
t[k].r=r;
if(l==r)
{
t[k].mx=s[l];
t[k].mi=s[l];
return ;
}
ll mid=(l+r)/2;
build(l,mid,k*2);
build(mid+1,r,k*2+1);
t[k].mi=min(t[k*2].mi,t[k*2+1].mi);
t[k].mx=max(t[k*2].mx,t[k*2+1].mx);
}
ll ask1(ll k)
{
if(t[k].l>=x&&t[k].r<=y){
return t[k].mi;
}
ll mii=inf;
ll mid=(t[k].l+t[k].r)/2;
if(x<=mid) mii=min(mii,ask1(k*2));
if(y>mid) mii=min(mii,ask1(k*2+1));
return mii;
}
ll ask2(ll k)
{
if(t[k].l>=x&&t[k].r<=y){
return t[k].mx;
}
ll mxx=-inf;
ll mid=(t[k].l+t[k].r)/2;
if(x<=mid) mxx=max(mxx,ask2(k*2));
if(y>mid) mxx=max(mxx,ask2(k*2+1));
return mxx;
}
}T;
int main()
{
scanf("%lld",&n);
for(ll i=1;i<=n;i++)
scanf("%lld",&a[i]);
s[0]=0;
for(ll i=1;i<=n;i++){
scanf("%lld",&b[i]);
s[i]=s[i-1]+b[i];
}
T.build(1,n,1);
for(ll i=1;i<=n;i++){
while(!sl.empty()&&a[sl.top()]>=a[i]) sl.pop();
if(sl.empty()) l[i]=0;
else l[i]=sl.top();
sl.push(i);
}
for(ll i=n;i>0;i--){
while(!sr.empty()&&a[sr.top()]>=a[i]) sr.pop();
if(sr.empty()) r[i]=n+1;
else r[i]=sr.top();
sr.push(i);
}
ll mx=-inf,vl,vr;
for(ll i=1;i<=n;i++){
if(a[i]>=0){
mx=max(mx,a[i]*(s[r[i]-1]-s[l[i]]));
}
else{
T.x=l[i],T.y=i-1;
vl=T.ask2(1);
T.x=i,T.y=r[i]-1;
vr=T.ask1(1);
mx=max(mx,a[i]*(vr-vl));
}
}
printf("%lld\n",mx);
return 0;
}
D:https://ac.nowcoder.com/acm/contest/884/D
给出一个数,用最少数量的3的倍数配凑出该数,运算为或。
• 分类讨论。
• 一个二进制位mod 3只可能是1或者2。
• 如果a是3的倍数,那么我们直接取{a}即可。
• 否则如果a的二进制位只有一位或两位,我们根本取不出0以外的三的倍数,所以无解。
• 接下来考虑a至少有三位的情况。
• 若a mod 3=1:
• 如果a中的二进制位有至少两个mod 3=1的,设它们为p和q,我们取{a-p,a-q}即可。
• 如果a中的二进制位有恰好一个mod 3=1的,那么设mod 3=1的这个位为p,mod 3=2
的某个位为q,我们取{a-p,p+q}即可。
• 如果a中的二进制位没有mod 3=1的,那么假设有三个mod 3=2的位p,q,r,我们取{a-
p-q,p+q+r}即可。
• 若a mod 3=2只需把上面的讨论中1与2互换即可,是完全对称的。
把3的倍数的数找到,其他的罗列出来,看区别,就可以想到消掉某一位1,2,4,8等等。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<ll> v[3];
int main()
{
ll t,n;
scanf("%lld",&t);
while(t--){
scanf("%lld",&n);
for(ll i=0;i<3;i++) v[i].clear();
if(n%3==0){
printf("1 %lld\n",n);
continue;
}
ll m=n,vv=1;
//记录哪个位置有1
while(m){
if(m%2){
v[vv%3].push_back(vv);
}
vv*=2;
m/=2;
}
if(n%3==1){
if(v[1].size()>=2){
printf("2 %lld %lld\n",n-v[1][0],n-v[1][1]);
}
else if(v[1].size()==1&&v[2].size()){
printf("2 %lld %lld\n",n-v[1][0],v[1][0]+v[2][0]);
}
else if(v[1].size()==0&&v[2].size()>=3){
printf("2 %lld %lld\n",n-v[2][0]-v[2][1],v[2][0]+v[2][1]+v[2][2]);
}
}
else{
if(v[2].size()>=2){
printf("2 %lld %lld\n",n-v[2][0],n-v[2][1]);
}
else if(v[2].size()==1&&v[1].size()){
printf("2 %lld %lld\n",n-v[2][0],v[1][0]+v[2][0]);
}
else if(v[2].size()==0&&v[1].size()>=3){
printf("2 %lld %lld\n",n-v[1][0]-v[1][1],v[1][0]+v[1][1]+v[1][2]);
}
}
}
return 0;
}
J:https://ac.nowcoder.com/acm/contest/884/J
给出n个点,m条边,s起点,t终点,k可忽略掉的边权。求最小花费。分层图!?也不用,直接优先队列加bfs
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,m,s,t,k;
ll a,b,c,ans;
struct edge
{
ll dian,cost;
};
struct node
{
ll dian,cnt,sum,pre;
bool friend operator < (node x,node y){
if(x.sum!=y.sum)
return x.sum>y.sum;
else
return x.cnt<y.cnt;
}
};
vector<edge> v[1005];
priority_queue<node> q;
void bfs(ll s)
{
q.push({s,k,0,0});
while(!q.empty()){
node x=q.top();
q.pop();
ll aa=x.dian,bb=x.cnt,cc=x.sum,dd=x.pre;
//printf("%lld %lld\n",bb,cc);
if(aa==t){
ans=cc;
return ;
}
for(ll i=0;i<v[aa].size();i++){
ll x=v[aa][i].dian,y=v[aa][i].cost;
if(x==aa||x==dd) continue;
if(bb){
q.push({x,bb-1,cc,aa});
}
q.push({x,bb,cc+y,aa});
}
}
}
int main()
{
scanf("%lld%lld%lld%lld%lld",&n,&m,&s,&t,&k);
for(ll i=0;i<m;i++){
scanf("%lld%lld%lld",&a,&b,&c);
v[a].push_back({b,c});
v[b].push_back({a,c});
}
bfs(s);
printf("%lld\n",ans);
return 0;
}
K:https://ac.nowcoder.com/acm/contest/884/K
给出一串字符串,求子串是300的倍数的数量,包括0.
300的倍数满足既是3的倍数,也是100的倍数。
用前缀和处理s[r]-s[l-1]=0,且s[r]、s[r+1]为0,则计算。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
char s[100005];
ll sum=0,cnt[5];
int main()
{
ll ans=0;
scanf("%s",s+1);
memset(cnt,0,sizeof(cnt));
ll len=strlen(s+1);
cnt[0]=1;//一定要注意最开始,什么都没有前缀和为0,有一个0!
for(ll i=1;i<=len;i++){
sum+=s[i]-'0';
sum%=3;
if(s[i]=='0') ans++;
if(i+1<=len&&s[i]=='0'&&s[i+1]=='0'){
ans+=cnt[sum];
}
cnt[sum]++;
}
printf("%lld\n",ans);
return 0;
}