给一棵n个点的图(保证是树)
给k个点表示k个点上有人,每次走一条边花费1分钟,问所有人走到同一个点最少要多少分钟
假设任意两个点间距离最大值为d,则d/2向上取整就是答案
最大距离求法类似求树的直径,第一次随便找一个点bfs找到距离当前点最远的点
然后从找到的点在bfs一次找到另外一个最远的点,两个最远点之间的距离就是最大距离
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef long long ll;
const int inf=0x3f3f3f3f;
const int inn=0x80808080;
using namespace std;
const int maxm=1e5+5;
vector<int>g[maxm];
map<int,int>have;
int kk[maxm];
int mark[maxm];
int ans=0;//最远距离
int id=1;//第一次bfs最远的点
void bfs(int st){
memset(mark,0,sizeof mark);
queue<int>q;
q.push(st);
mark[st]=1;
int timee=0;//距离
while(!q.empty()){
int len=q.size();
for(int i=0;i<len;i++){
int x=q.front();
q.pop();
if(have[x]){//如果标记了,尝试更新
if(timee>ans){
ans=timee;
id=x;
}
}
for(int j=0;j<(int)g[x].size();j++){
int v=g[x][j];
if(!mark[v]){
mark[v]=1;
q.push(v);
}
}
}
timee++;
}
}
int main(){
int n,k;
cin>>n>>k;
for(int i=1;i<=n-1;i++){
int a,b;
cin>>a>>b;
g[a].push_back(b);
g[b].push_back(a);
}
for(int i=1;i<=k;i++){
cin>>kk[i];
have[kk[i]]=1;//标记
}
bfs(kk[1]);//随便找一个点bfs就行
bfs(id);//bfs前面找到的最远的点
cout<<(ans+1)/2<<endl;//最远距离的一半就是答案,向上取整
return 0;
}
给一个数a,问a能否由多个3的倍数的数通过与运算(&)得来
例如7=3&6
要求数字个数尽量少
答案最多就两位。
一个二进制位mod 3只可能是1或者2。
若a是3的倍数,那么直接取a即可。
如果a的二进制位只有一位或两位,根本取不出0以外的3的倍数,所以无解。
官方题解:
只需考虑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,我们取ap-q,p+q+r即可。
若a mod 3=2只需把上面的讨论中1与2互换即可,是完全对称的。
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef long long ll;
const int inf=0x3f3f3f3f;
const int inn=0x80808080;
using namespace std;
ll n;
void solve(){
vector<ll>a[3];
for(int i=0;i<=60;i++){
if(n>>i&1){//%3=1的放到a[1],%3=2的放到a[2]
a[(i&1)+1].push_back(1LL<<i);//数字1必须要加LL不然会被认为是int类型
}
}
if(n%3==1){
if(a[1].size()>=2){
cout<<2<<' '<<n-a[1][0]<<' '<<n-a[1][1]<<endl;
}else if(a[1].size()==1){
cout<<2<<' '<<n-a[1][0]<<' '<<a[1][0]+a[2][0]<<endl;
}else{
cout<<2<<' '<<n-a[2][0]-a[2][1]<<' '<<a[2][0]+a[2][1]+a[2][2]<<endl;
}
}else{
if(a[2].size()>=2){
cout<<2<<' '<<n-a[2][0]<<' '<<n-a[2][1]<<endl;
}else if(a[1].size()==1){
cout<<2<<' '<<n-a[2][0]<<' '<<a[2][0]+a[1][0]<<endl;
}else{
cout<<2<<' '<<n-a[1][0]-a[1][1]<<' '<<a[1][0]+a[1][1]+a[1][2]<<endl;
}
}
}
int main(){
int T;
cin>>T;
while(T--){
cin>>n;
if(n%3==0){
cout<<1<<' '<<n<<endl;
}else{
solve();
}
}
return 0;
}
给n,m,s,t,k
表示n个点,m条带权边,s起点,t终点,k表示k条边免费
每条边都要花费
求s到t最小需要花多少钱
有模板题:P4568 [JLOI2011]飞行路线
正解:分层最短路
自己做的时候跑一边最短路,跑的过程种记录最短路径的边,跑完取出权值排序,把除了k个最大的加起来,没想到直接a了,
想了想,感觉如果有两种总权值相同边不同的最短路好像就不行了。
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef long long ll;
const int inf=0x3f3f3f3f;
const int inn=0x80808080;
using namespace std;
const int N=1e6+5;
const int M=5e6+5;
int head[N],nt[M],to[M],w[M];
int cnt;
int dis[N];
int n,m,k,s,t;
void init(){
memset(head,-1,sizeof head);
cnt=0;
}
void add(int x,int y,int z){
cnt++;nt[cnt]=head[x];head[x]=cnt;to[cnt]=y;w[cnt]=z;
}
void dj(){
priority_queue<pair<int,int> ,vector<pair<int,int> >,greater<pair<int,int> > >q;
memset(dis,inf,sizeof dis);
dis[s]=0;
q.push(make_pair(dis[s],s));
while(!q.empty()){
int x=q.top().second;
q.pop();
for(int i=head[x];i!=-1;i=nt[i]){
int v=to[i];
if(dis[v]>dis[x]+w[i]){
dis[v]=dis[x]+w[i];
q.push(make_pair(dis[v],v));
}
}
}
}
int main(){
while(scanf("%d%d%d",&n,&m,&k)==3){
scanf("%d%d",&s,&t);
init();
while(m--){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
for(int i=0;i<=k;i++){
add(u+i*n,v+i*n,w);//当前层
add(v+i*n,u+i*n,w);
if(i!=k){//连到下一层
add(u+i*n,v+(i+1)*n,0);//到下一层的花费是0
add(v+i*n,u+(i+1)*n,0);
}
}
}
dj();
int ans=inf;
for(int i=0;i<=k;i++){
ans=min(ans,dis[t+i*n]);
}
printf("%d\n",ans);
}
return 0;
}
给一串数字问有多少子串是300的倍数
0和00和多个0也算
300的倍数即0前面是3的倍数后面至少两个0的数
官方题解修改:
记s[i]为前i的和mod3的值(前缀和),对于长度大于等于2的区间[l,r],如果s[l-1]=s[r](这样表明[l,r]里面的和mod3等于0,也就是3的倍数),如果此时s[r]和s[r-1]等于0,那么满足条件,累加答案
从小到大枚举r,同时记录有多少个0<=l<=r-2的l满足s[l]=0,1,2
复杂度O(n)
#include
#include
#include
#include
#include
#include
#include
#include
#include
typedef long long ll;
const int inf=0x3f3f3f3f;
const int inn=0x80808080;
using namespace std;
const int maxm=1e5+5;
char s[maxm];
int sum[maxm];
int cnt[4];//[l,r-2]中0,1,2的个数,
int main(){
scanf("%s",s+1);
int len=strlen(s+1);
for(int i=1;i<=len;i++){
sum[i]=(sum[i-1]+s[i]-'0')%3;
}
ll ans=0;
for(int i=1;i<=len;i++){
if(s[i]=='0'){
ans++;
if(s[i-1]=='0'){
ans+=cnt[sum[i]];
}
}
cnt[sum[i-1]]++;
}
cout<<ans<<endl;
return 0;
}