A-meeting
题目链接:https://ac.nowcoder.com/acm/contest/884/A
题目大意:n个点的树。k个人分别在点Xk,求出所有人都到一个点的最短花费时间。
思路:将所有多余的树枝都砍掉,两次DFS。剩下的都是有用的。求剩下的这棵树的直径,因为直径上的两端点必定是最长的时间花费,因此两次DFS求出直径后,直接计算直径即可。
ACCode:
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<time.h>
#include<math.h>
// srand(unsigned)time(NULL));rand();
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
#define ll long long
#define Pair pair<int,int>
#define clean(a,b) memset(a,b,sizeof(a))
using namespace std;
const int MAXN=1e5+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)
struct Node{
int v,val,nxt;
Node(int _v=0,int _val=0,int _nxt=0){
v=_v;val=_val;nxt=_nxt;
}
};
Node Edge[MAXN<<2];
int Head[MAXN],Ecnt;
int Deep[MAXN],Val[MAXN],Sum[MAXN],Vis[MAXN];
int res,ans,n,m;
void Intt(){
clean(Head,-1);Ecnt=0;
clean(Deep,0);clean(Val,0);clean(Sum,0);
}
void AddEdge(int u,int v,int val){
Edge[Ecnt]=Node(v,val,Head[u]);
Head[u]=Ecnt++;
}
void DFS(int u,int fa,int dep=0){
Vis[u]=1;
Deep[u]=dep;
Sum[u]=Val[u];
for(int i=Head[u];i+1;i=Edge[i].nxt){
int v=Edge[i].v;
if(v==fa) continue;
if(Edge[i].val==0) continue;
DFS(v,u,dep+1);Sum[u]+=Sum[v];
if(Sum[v]==0){//子节点没有贡献 ,砍掉
Edge[i].val=0;
}
}
}
int main(){
Intt();
scanf("%d%d",&n,&m);
for(int i=1;i<n;++i){
int u,v;scanf("%d%d",&u,&v);
AddEdge(u,v,1);AddEdge(v,u,1);
}
for(int i=1;i<=m;++i){
int x;scanf("%d",&x);Val[x]=1;
}DFS(1,0);//砍树
int rt=1;
clean(Vis,0);DFS(rt,0);//标记。
for(int i=1;i<=n;++i){//找根
if(Vis[i]&&Deep[i]>Deep[rt]) rt=i;
}
// printf("rt=%d\n",rt);
clean(Sum,0);clean(Deep,0);clean(Vis,0);
DFS(rt,0);//换根继续砍
clean(Deep,0);
DFS(rt,0); //求深度
int d=0;
for(int i=1;i<=n;++i) d=max(d,Deep[i]);
// printf("d=%d\n",d);
int Ans=d%2?d/2+1:d/2;
printf("%d\n",Ans);
}
B-xor
题目链接:https://ac.nowcoder.com/acm/contest/884/B
数论队友线性基:https://ac.nowcoder.com/acm/contest/884/B
C-sequence
题目链接:https://ac.nowcoder.com/acm/contest/884/C
题目大意:给出两个序列a,b,找出找出最大的值:1≤l≤r≤n max{min(al…r)×sum(bl…r)}
思路:有点类似上半年南昌邀请网络赛https://blog.csdn.net/qq_40482358/article/details/89422999
枚举最小的a[i],确定a[i]的最小区间范围。找到范围内的最大的前缀和和最小的前缀和即可。
a[i]的最小的范围可以用单调栈来维护,从前到后和从后到前分别维护单增的栈。
ACCode:
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<time.h>
#include<math.h>
// srand(unsigned)time(NULL));rand();
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
#define ll long long
#define Pair pair<int,int>
#define clean(a,b) memset(a,b,sizeof(a))
using namespace std;
const int MAXN=3e6+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)
struct SegTree{
ll Max[MAXN<<2],Min[MAXN<<2];
void Build(int l,int r,int rt,ll A[]){
if(l==r){
Max[rt]=Min[rt]=A[l];
return ;
}
int mid=(l+r)>>1;
Build(l,mid,rt<<1,A);Build(mid+1,r,rt<<1|1,A);
Max[rt]=max(Max[rt<<1],Max[rt<<1|1]);
Min[rt]=min(Min[rt<<1],Min[rt<<1|1]);
}
ll QueryMax(int ql,int qr,int l,int r,int rt){
if(ql<=l&&r<=qr) return Max[rt];
int mid=(l+r)>>1;
ll res=-INF64;
if(ql<=mid) res=max(res,QueryMax(ql,qr,l,mid,rt<<1));
if(qr>mid) res=max(res,QueryMax(ql,qr,mid+1,r,rt<<1|1));
return res;
}
ll QueryMin(int ql,int qr,int l,int r,int rt){
if(ql<=l&&r<=qr) return Min[rt];
int mid=(l+r)>>1;
ll res=INF64;
if(ql<=mid) res=min(res,QueryMin(ql,qr,l,mid,rt<<1));
if(qr>mid) res=min(res,QueryMin(ql,qr,mid+1,r,rt<<1|1));
return res;
}
};
SegTree Seg;//,SegMin;
ll A[MAXN],B[MAXN];
ll Min[MAXN][2],SumB[MAXN];
int Stk[MAXN],Index[MAXN],top;
int n;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%lld",&A[i]);
for(int i=1;i<=n;++i) scanf("%lld",&B[i]);
top=0;
for(int i=1;i<=n;++i){
while(top>0&&A[i]<Stk[top]) --top;
if(top==0) Min[i][0]=1;
else Min[i][0]=Index[top]+1;
Stk[++top]=A[i];Index[top]=i;
}
top=0;
for(int i=n;i>=1;--i){
while(top>0&&A[i]<Stk[top]) --top;
if(top==0) Min[i][1]=n;
else Min[i][1]=Index[top]-1;
Stk[++top]=A[i];Index[top]=i;
}
for(int i=2;i<=n+1;++i) SumB[i]=SumB[i-1]+B[i-1];SumB[1]=0;
Seg.Build(1,n+1,1,SumB);
ll Ans=-INF64;
for(int i=1;i<=n;++i){//对于每个点,求出他的影响区间,如果该点为负数,左右找打最小的前缀和,正数找到最大的Sum
int l=Min[i][0],r=Min[i][1];
l++;r++;//区间向后移动一位
l--;//查询区间[l-1,i][i,r];
ll Suml,Sumr;
if(A[i]<0){
Suml=Seg.QueryMax(l,i+1,1,n+1,1);
Sumr=Seg.QueryMin(i+1,r,1,n+1,1);
}
else{
Suml=Seg.QueryMin(l,i+1,1,n+1,1);
Sumr=Seg.QueryMax(i+1,r,1,n+1,1);
}
//printf("l=%d r=%d\n",l,r);
//printf("Suml=%lld Sumr=%lld\n",Suml,Sumr);
Suml=SumB[i]-Suml;Sumr=Sumr-SumB[i];
//printf("Suml=%lld Sumr=%lld\n",Suml,Sumr);
Ans=max(Ans,1ll*(Suml+Sumr)*A[i]);
//printf("Ans=%lld\n",Ans);
}printf("%lld\n",Ans);
}
D-triples I
题目链接:https://ac.nowcoder.com/acm/contest/884/D
数论队友:https://blog.csdn.net/henucm/article/details/97758303
E-triples II
题目链接:https://ac.nowcoder.com/acm/contest/884/E
数论队友:https://blog.csdn.net/henucm/article/details/102639665
J-free
题目链接:https://ac.nowcoder.com/acm/contest/884/J
题目大意:一个图,可以走k次近道。稳到达终点的最小花费。
思路:分层最短路板子题
ACCode:
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<time.h>
#include<math.h>
// srand(unsigned)time(NULL));rand();
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
#define ll long long
#define Pair pair<int,int>
#define clean(a,b) memset(a,b,sizeof(a))
using namespace std;
const int MAXN=1e3+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)
struct Node{
int v,val,nxt;
Node(int _v=0,int _val=0,int _nxt=0){
v=_v;val=_val;nxt=_nxt;
}
};
Node Edge[MAXN<<2];
int Head[MAXN],Ecnt;
int Vis[MAXN][MAXN],Dis[MAXN][MAXN];
int n,m,S,T,k;
void Intt(){
clean(Head,-1);Ecnt=0;
clean(Vis,0);clean(Dis,INF32);
}
void AddEdge(int u,int v,int val){
Edge[Ecnt]=Node(v,val,Head[u]);
Head[u]=Ecnt++;
}
#define Piii pair<int,Pair>
#define M_P(a,b,c) make_pair(a,make_pair(b,c))
void Dijkstra(){
Dis[S][0]=0;
priority_queue<Piii> que;que.push(M_P(0,S,0));
while(que.size()){
while(Vis[que.top().second.first][que.top().second.second]&&que.size()>0) que.pop();
Pair p=que.top().second;
Vis[p.first][p.second]=1;
for(int i=Head[p.first];i+1;i=Edge[i].nxt){//同层查找
int v=Edge[i].v;
if(Vis[v][p.second]==0&&Dis[v][p.second]>Dis[p.first][p.second]+Edge[i].val){
Dis[v][p.second]=Dis[p.first][p.second]+Edge[i].val;
que.push(M_P(-Dis[v][p.second],v,p.second));
}
if(p.second+1<=k&&Vis[v][p.second+1]==0&&Dis[v][p.second+1]>Dis[p.first][p.second]){
Dis[v][p.second+1]=Dis[p.first][p.second];
que.push(M_P(-Dis[v][p.second+1],v,p.second+1));
}
}
}
}
int main(){
Intt();
scanf("%d%d%d%d%d",&n,&m,&S,&T,&k);
for(int i=1;i<=m;++i){
int u,v,val;scanf("%d%d%d",&u,&v,&val);
AddEdge(u,v,val);AddEdge(v,u,val);
}Dijkstra();
int Ans=INF32;
for(int i=0;i<=k;++i){
Ans=min(Ans,Dis[T][i]);
}printf("%d\n",Ans);
}
K-number
题目链接:https://ac.nowcoder.com/acm/contest/884/K
题目大意:给出一串数字,判断有多少个字符串是300的倍数。
思路:求出前缀之和%3的余数。求出对应的个数。从后向前遍历字符串,cnt表示连续0的个数。
当出现一个不为0的数时,判断该位前面有多少个相同的数(相同的数表示中间相差的有3的倍数)加上后面0的个数组成数量。
ACCode:
#include<stdlib.h>
#include<string.h>
#include<stdio.h>
#include<time.h>
#include<math.h>
// srand(unsigned)time(NULL));rand();
#include<map>
#include<set>
#include<deque>
#include<queue>
#include<stack>
#include<bitset>
#include<string>
#include<fstream>
#include<iostream>
#include<algorithm>
#define ll long long
#define Pair pair<int,int>
#define clean(a,b) memset(a,b,sizeof(a))
using namespace std;
const int MAXN=1e5+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll MOD=1e9+7;
const double PI=acos(-1.0);
const double EPS=1.0e-8;
//unsigned register
// ios::sync_with_stdio(false)
char S[MAXN];
int Sum[MAXN];
int A[4][MAXN];
int main(){
scanf("%s",S+1);
int len=strlen(S+1);//printf("len=%d\n",len);
for(int i=1;i<=len;++i){
Sum[i]=Sum[i-1]+S[i]-'0';
Sum[i]%=3;
}
A[0][0]++;
for(int i=1;i<=len;++i){
for(int j=0;j<=2;++j) A[j][i]=A[j][i-1];
A[Sum[i]][i]++;
}
// for(int i=0;i<=2;++i){
// for(int j=0;j<=len;++j){
// printf(" %d",A[i][j]);
// }printf("\n");
// }
ll Ans=0;
int cnt=0;
for(int i=len;i>=0;--i){
if(S[i]=='0') cnt++;
else{
if(cnt>=2){//Ans+
int x=Sum[i];
int num=A[x][i];
num-=1;
Ans+=1ll*(cnt-1)*num;
Ans+=(1ll+cnt)*cnt/2;
}
if(cnt==1) Ans++;
cnt=0;
}
// printf("i=%d Ans=%lld\n",i,Ans);
}printf("%lld\n",Ans);
}