2019牛客暑期多校训练营(第四场

持续  更新

meeting

答案为最远关键点的距离的一半向上取整,也就是关键点的树的直径的一半向上取整。

先考虑两个点,他俩是最远距离,那么最短时间就是⌈d/2⌉,在此基础上再加一个点(前提是加上这个点,后不影响初始条件,即初始的两个点之间的距离最远),那么不会影响答案,因为他与另外两个点的相会的时间必然小于⌈d/2⌉,这个题是最小化最大值。

所以答案是关键点的树的直径的一半向上取整.

求树的直径的方法有两半BFS,两边DFS,书上DP。

另附两遍BFS的代码:

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define Pair pair<int, int>
#define int long long
#define fir first
#define sec second
namespace IO {
  const int maxn=14096;char buf[maxn],t[50];int bn=maxn,bi=maxn;
  int read(char* s) {
    while(bn){for(;bi<bn&&buf[bi]<=' ';bi++);if(bi<bn) break;bn=fread(buf, 1, maxn, stdin);bi=0;}
    int sn=0;
    while(bn){for(;bi<bn&&buf[bi]>' ';bi++)s[sn++]=buf[bi];if (bi<bn) break;bn=fread(buf, 1, maxn, stdin);bi=0;}
    s[sn]=0;return sn;
  }
  bool read(int& x) {if (!read(t)) return 0;x=atoi(t);return 1;}
}using namespace IO;
using namespace std;
const int N=1e5+5,M=2e5+5;
int head[N],ver[M],Next[M],tot;
bool use[N],co[N];int R;
void add(int x,int y){ver[++tot]=y;Next[tot]=head[x];head[x]=tot;}
  
int bfs(int u){
  int last=u,num=0;
  queue<int>q;
  q.push(u);use[u]=1;
  while(q.size()){
    int wide=q.size();num++;
    while(wide--){
      int x=q.front();q.pop();
      for(int i=head[x];~i;i=Next[i]){
        int y=ver[i];
        if(use[y]) continue;q.push(y);
        if(co[y]){R=num;last=y;}
        use[y]=1;
      }
    }
  }
  return last;
}
signed main(){
  ;;;;;;;;;;;;;;;;;;;;;;;
  int n,k;read(n);read(k);
  for(int i=0;i<N;i++)head[i]=-1;
  for(int i=1;i<=n-1;i++){
    int x,y;read(x);read(y);
    add(x,y);add(y,x);
  }
  int fi;
  for(int i=1;i<=k;i++){
    int x;read(x);co[x]=1;fi=x;
  }
  int last=bfs(fi);
  for(int i=0;i<N;i++)use[i]=0;
  last=bfs(last);
  printf("%lld\n",(R+1)/2);
      
  return 0;
}

 

triples I

基本是官方题解,我进行简单的举例。

首先我们知道每一个二进制位mod3只有1或者2两种情况,我们简单举个例子:

8 4 2 1  mod3 分别为2 1 2 1

前若干位512 256 128 64 32 16 8 4 2 1

模数:      2     1     2    1   2   1  2 1 2 1

如果a是3的倍数,那么我们直接输出a,不然结果必为两个数相或为结果

如果a的二进制只有一位或两位,不存在两个3的倍数或之后结果为a。

接下来考虑a至少有三位的情况。

若a mod 3=1:

•如果a中的二进制位有至少两个mod 3=1的,设它们为p和q,我们取{a-p,a-q}即可。

加入a为13 (8+4+1) 对应模数为(2+1      +1),多出来的1导致了结果mod 3=1,我们用a减去这个1就是3的倍数,此时这个3的倍数的被减去的二进制位为0,可以通过减去另一个1,产生的另一个3的倍数,两者相或可以弥补二进制位置。

• 如果a中的二进制位有恰好一个mod 3=1的,那么设mod 3=1的这个位为p,mod 3=2 的某个位为q,我们取{a-p,p+q}即可。

如a= 43 (32+8+2+1)对应模数为(2+2+2      +1),多出来的1导致了结果mod 3=1,剪掉这个 1,则产生第一个3的倍数,这个1随便加上一个2,也是3的倍数,同时可相互弥补二进制中的位数,

• 如果a中的二进制位没有mod 3=1的,那么假设有三个mod 3=2的位p,q,r,我们取{a-p-q,p+q+r}即可。

如a= 682 (512+128+32+8+2)对应模数为(2+2+2      +2+2),多出来的4导致了结果mod 3=1,剪掉这个 2+2,则产生第一个3的倍数,这两个2随便加上一个2,也是3的倍数,同时可相互弥补二进制中的位数,

• 若a mod 3=2只需把上面的讨论中1与2互换即可,是完全对称的。

我们简单分别举一下例子:

至少两个mod 3=2:

如a= 14 (8+4+2)对应模数为(2+1      +2),多出来的2导致了结果mod 3=2,剪掉这个2,则产生第一个3的倍数,这个2随便加上一个1,也是3的倍数,同时可相互弥补二进制中的位数,

恰好一个mod 3=2的:

如a= 85 (64+16+4+1)对应模数为(1+1+1      +2),多出来的2导致了结果mod 3=2(也可理解为多出来的1+1 导致mod3=2),剪掉这个2,则产生第一个3的倍数,这个2随便加上一个1,也是3的倍数,同时可相互弥补二进制中的位数,

没有mod 3=1的:

如a= 85 (64+16+4+1)对应模数为(1+1+1      +1+1),多出来的1+1导致了结果mod 3=2,剪掉这个(1+1),则产生第一个3的倍数,这两个1随便加上一个1,也是3的倍数,同时可相互弥补二进制中的位数,

另附代码:

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define Pair pair<int,int>
#define int long long
#define fir first
#define sec second
namespace IO {
  const int maxn=14096;char buf[maxn], t[50];int bn=maxn,bi=maxn;
  int read(char* s) {
    while(bn){for(;bi<bn&&buf[bi]<=' ';bi++);if(bi<bn)break;bn=fread(buf,1,maxn,stdin);bi=0;}int sn=0;
    while(bn){for(;bi<bn&&buf[bi]>' ';bi++)s[sn++]=buf[bi];if(bi<bn)break;bn=fread(buf,1,maxn,stdin);bi=0;}s[sn]=0;return sn;
  }
  bool read(int& x){if(!read(t))return 0;x=atoi(t);return 1;}
}
using namespace std;
using namespace IO;
//const int N=1e8+5;
const int mod=1e9+7;
 
vector<int>one,two;
void f(int x){
  one.clear();two.clear();
  int base=1;
  while(x){
    if(x&1){
      if(base%3==1)one.push_back(base);
      else two.push_back(base);
    }
    base*=2;
    x>>=1;
  }
}
 
signed main(){
  int t;cin>>t;
  while(t--){
    int n;cin>>n;f(n);
    if(n%3==0)printf("%lld %lld\n",1,n);
    else{
      if(n%3==1){
        if(one.size()>=2)printf("%lld %lld %lld\n",2,n-one[0],n-one[1]);
        else{
          if(one.size()==1)printf("%lld %lld %lld\n",2,n-one[0],one[0]+two[0]);
          else printf("%lld %lld %lld\n",2,n-two[0]-two[1],two[0]+two[1]+two[2]);
        }
      }else{
        if(two.size()>=2)printf("%lld %lld %lld\n",2,n-two[0],n-two[1]);
        else{
          if(two.size()==1)printf("%lld %lld %lld\n",2,n-two[0],one[0]+two[0]);
          else printf("%lld %lld %lld\n",2,n-one[0]-one[1],one[0]+one[1]+one[2]);
        }       
      }
    }
  }
  return 0;
}

K number

后缀和找规律,

简单附代码

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define Pair pair<int,int>
#define int long long
#define fir first
#define sec second
namespace IO {
  const int maxn=14096;char buf[maxn], t[50];int bn=maxn,bi=maxn;
  int read(char* s) {
    while(bn){for(;bi<bn&&buf[bi]<=' ';bi++);if(bi<bn)break;bn=fread(buf,1,maxn,stdin);bi=0;}int sn=0;
    while(bn){for(;bi<bn&&buf[bi]>' ';bi++)s[sn++]=buf[bi];if(bi<bn)break;bn=fread(buf,1,maxn,stdin);bi=0;}s[sn]=0;return sn;
  }
  bool read(int& x){if(!read(t))return 0;x=atoi(t);return 1;}
}
using namespace std;
using namespace IO;
const int N=1e6+5;
const int mod=1e9+7;
char str[N];
int num[N],m[N];
signed main(){
  read(str+1);
  int len=strlen(str+1);
  for(int i=1;i<=len;i++)num[i]=str[i]-'0';
  for(int i=len;i>=1;i--)num[i]=num[i]+num[i+1];
  int z=0,res=0;
  for(int i=1;i<=len;i++){
    if(str[i]=='0')z++;
    else{
      res+=(1+z)*z/2;z=0;
    }
  }
  if(z)res+=(1+z)*z/2;z=0;
   
  //printf("%lld\n",res);
   
  for(int i=len;i>=1;i--){
    if(str[i]=='0'){
      z++;
      //cout<<i<<" "<<z<<endl;
    }
    else{
      if(z>=2)m[num[i+1]%3]+=z-1;
      z=0;
    }
    res+=m[num[i]%3];
  }
  printf("%lld\n",res);
     
  return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值