A.meeting
传送:https://ac.nowcoder.com/acm/contest/884/A
题意:有$n$个结点的树,$k$个人在一些结点上,问他们到某一点集合,问某一个人走的最长的距离为多少。
数据范围:$1<=n<=10^5$。
分析:考虑有人的叶子结点最远的那个就好。把没有人的叶子节点去掉,然后求一个树的直径。答案是$lceil d/2 ceil$。
1 #include<bits/stdc++.h> 2 #define pb push_back 3 using namespace std; 4 const int maxn=1e5+7; 5 vector<int> g[maxn*2]; 6 int tag[maxn],vis[maxn]; 7 int dis[maxn]; 8 int bfs(int x) 9 { 10 queue<int> Q; 11 for (int i=0;i<maxn;i++) vis[i]=dis[i]=0; 12 Q.push(x);vis[x]=1; 13 int y=x; 14 while (!Q.empty()) 15 { 16 int cur=Q.front();Q.pop(); 17 for (auto i:g[cur]) 18 { 19 if(!vis[i]) 20 { 21 dis[i]=dis[cur]+1; 22 vis[i]=1; 23 Q.push(i); 24 if(tag[i]) y=i; 25 } 26 } 27 } 28 return y; 29 } 30 int main() 31 { 32 int n,k; 33 scanf("%d%d",&n,&k); 34 for (int i=0;i<maxn;i++) tag[i]=0; 35 for (int i=1;i<n;i++) 36 { 37 int u,v;scanf("%d%d",&u,&v); 38 g[u].pb(v);g[v].pb(u); 39 } 40 int S; 41 for (int i=1;i<=k;i++) 42 { 43 int x;scanf("%d",&x); 44 if(i==k) S=x; 45 tag[x]=true; 46 } 47 if(k==0) printf("0 "); 48 else 49 { 50 int T=bfs(S);S=bfs(T); 51 int ans=(dis[S]+1)/2; 52 printf("%d ",ans); 53 } 54 return 0; 55 }
B.xor
传送:https://ac.nowcoder.com/acm/contest/884/B
题意:有$n$个集合,每个集合内有$k$个数。$m$个询问:询问区间$[l,r]$内的每一个区间中是否可以选择若干个数异或和为$x$。
数据范围:$1<=n,m<=5e4,1<=k<=32,1<=x<=2^{32}$。
分析:判断一个数是否可以由集合中的若干个数构成,线性基模板。
然后考虑区间内每个集合是否都满足,暴力$for l->r$一定会tle。考虑用线段树维护区间线性基的交。然后每次区间内$check$数字$x$是否可以被插入到这个区间的线性基(交)内即可。
qaaaaq,板子+板子。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn=5e4+10; 5 struct Linear_basis{ 6 ll b[35]; 7 int insert(ll x){ 8 for (int i=32;i>=0;i--){ 9 if (x&(1ll<<i)){ 10 if (!b[i]){ 11 b[i]=x; break; 12 } 13 x^=b[i]; 14 } 15 } 16 return x>0; 17 } 18 int checkin(ll x){ 19 for (int i=32;i>=0;i--){ 20 if (x&(1ll<<i)){ 21 if (!b[i]) break; 22 x^=b[i]; 23 } 24 } 25 return x>0; 26 } 27 void clear(){ 28 memset(b,0,sizeof(b)); 29 } 30 }LB[maxn],ans; 31 struct node{ 32 int l,r; 33 Linear_basis LB; 34 }tree[maxn<<2]; 35 Linear_basis intersection(Linear_basis p,Linear_basis q){ 36 Linear_basis ans,c=q,d=q; ans.clear(); 37 for (int i=0;i<=32;i++){ 38 ll x=p.b[i]; 39 if(!x) continue; 40 int j=i; 41 ll tmp=0; 42 for(;j>=0;j--){ 43 if((x>>j)&1){ 44 if(c.b[j]) {x^=c.b[j];tmp^=d.b[j];} 45 else break; 46 } 47 } 48 if(!x) ans.b[i]=tmp; 49 else {c.b[j]=x;d.b[j]=tmp;} 50 } 51 return ans; 52 } 53 void pushup(int root){ 54 tree[root].LB=intersection(tree[root<<1].LB,tree[root<<1|1].LB); 55 } 56 void build(int root,int l,int r){ 57 tree[root].l=l;tree[root].r=r; tree[root].LB.clear(); 58 if (l==r){ 59 tree[root].LB=LB[l]; 60 return ; 61 } 62 int mid=(l+r)>>1; 63 build(root<<1,l,mid); 64 build(root<<1|1,mid+1,r); 65 pushup(root); 66 } 67 int query(int root,int xx,int yy,ll val){ 68 if (xx<=tree[root].l && tree[root].r<=yy){ 69 int ff=tree[root].LB.checkin(val); //check是否可插入 70 return ff; 71 } 72 int mid=(tree[root].l+tree[root].r)>>1; 73 int f=0; 74 if (xx<=mid) f|=query(root<<1,xx,yy,val); 75 if (yy>mid) f|=query(root<<1|1,xx,yy,val); 76 return f; 77 } 78 int main(){ 79 int n,m;scanf("%d%d",&n,&m); 80 ll x; int k,l,r; 81 for (int i=1;i<=n;i++){ 82 scanf("%d",&k); 83 for (int j=1;j<=k;j++){ 84 scanf("%lld",&x); 85 LB[i].insert(x); 86 } 87 } 88 build(1,1,n); 89 while (m--){ 90 scanf("%d%d%lld",&l,&r,&x); 91 int f=query(1,l,r,x); 92 if (f) printf("NO "); //可插入,不可构成 93 else printf("YES "); 94 } 95 return 0; 96 }
C.sequence
传送:https://ac.nowcoder.com/acm/contest/884/C
题意:给定两个数组$a_i,b_i$。求解:${max}_{1<=l<=r<=n}{min(a_{l..r}) imes sum(b_{l..r}}$。
数据范围:$1<=n<=3 imes 10^6,-10^6<=a_i,b_i<=10^6$。
分析:对于每个$a_i$考虑能有作为最小值覆盖的最长区域为多少。用单调栈维护。
同时,用线段树维护$b_i$的前缀和的最大值和最小值。
当$a_i>=0$时,答案就是$querymax(i,r[i])-querymin(l[i]-1,i-1)$,
否则:答案就是$querymin(i,r[i])-querymax(l[i]-1,i-1)$。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int N=3e6+5; 5 const ll INF=1e18; 6 int a[N],b[N],l[N],r[N],st[N];ll sum[N],ans; 7 int n; 8 struct node{ 9 int l,r; 10 ll mx,mi; 11 }tree[N<<2]; 12 void pushup(int root){ 13 tree[root].mi=min(tree[root<<1].mi,tree[root<<1|1].mi); 14 tree[root].mx=max(tree[root<<1].mx,tree[root<<1|1].mx); 15 } 16 void build(int root,int l,int r){ 17 tree[root].l=l; tree[root].r=r; tree[root].mi=INF; tree[root].mx=-INF; 18 if (l==r){ 19 tree[root].mi=tree[root].mx=sum[l]; 20 return ; 21 } 22 int mid=(l+r)>>1; 23 build(root<<1,l,mid); 24 build(root<<1|1,mid+1,r); 25 pushup(root); 26 } 27 ll query_max(int root,int x,int y){ 28 if (x<=tree[root].l && tree[root].r<=y){ 29 return tree[root].mx; 30 } 31 int mid=(tree[root].l+tree[root].r)/2; 32 ll res=-INF; 33 if (x<=mid) res=max(res,query_max(root<<1,x,y)); 34 if (mid<y) res=max(res,query_max(root<<1|1,x,y)); 35 return res; 36 } 37 ll query_min(int root,int x,int y){ 38 if (x<=tree[root].l && tree[root].r<=y){ 39 return tree[root].mi; 40 } 41 int mid=(tree[root].l+tree[root].r)/2; 42 ll res=INF; 43 if (x<=mid) res=min(res,query_min(root<<1,x,y)); 44 if (mid<y) res=min(res,query_min(root<<1|1,x,y)); 45 return res; 46 } 47 int q[N],id[N]; 48 void solve(){ 49 int cnt=0; 50 for(int i=1;i<=n;i++){ 51 while(cnt&&q[cnt]>=a[i]) cnt--; 52 l[i]=id[cnt]+1; 53 q[++cnt]=a[i],id[cnt]=i; 54 } 55 cnt=0;id[0]=n+1; 56 for(int i=n;i>=1;i--){ 57 while(cnt&&q[cnt]>=a[i]) cnt--; 58 r[i]=id[cnt]-1; 59 q[++cnt]=a[i],id[cnt]=i; 60 } 61 ll ans=-INF; 62 for (int i=1;i<=n;i++){ 63 if (a[i]>0){ 64 ll tmp=query_max(1,i,r[i])-query_min(1,l[i]-1,i-1); 65 ans=max(ans,1ll*a[i]*tmp); 66 } 67 else{ 68 ll tmp=query_min(1,i,r[i])-query_max(1,l[i]-1,i-1); 69 ans=max(ans,1ll*a[i]*tmp); 70 } 71 } 72 printf("%lld ",ans); 73 } 74 int main(){ 75 scanf("%d",&n); 76 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 77 for(int i=1;i<=n;i++) scanf("%d",&b[i]),sum[i]=sum[i-1]+b[i]; 78 build(1,0,n); 79 solve(); 80 return 0; 81 }
D.triples I
传送:https://ac.nowcoder.com/acm/contest/884/D
题意:给定一个$a$,问最少有几个$x$可以使他们的或和为$a$,且要求这些$x$是3的倍数。
数据范围:$1<=n<=10^{18}$。
分析:易得,如果$a%3==0$,答案一定只有一个数是其本身,否则一定是可以由两个数构成。
将$a$拆成二进制考虑,奇数位的位权%3==1,偶数位位权%3==2。那么就是变成有若干个1,若干个2。
拼凑出两个数使其都为3的倍数。
1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 ll x; 5 int num; int a[100],vis[100]; 6 int t1,t2; 7 vector<ll> v[3]; 8 int main(){ 9 int t;scanf("%d",&t); 10 while (t--){ 11 scanf("%lld",&x); 12 if (x%3==0){ 13 printf("1 %lld ",x); 14 continue; 15 } 16 ll xx=x; 17 t1=0;t2=0;num=0; 18 while(xx){ 19 a[++num]=(xx&1),xx/=2; 20 if (a[num]==1){ 21 if (num&1) t1++; 22 else t2++; 23 } 24 } 25 v[1].clear();v[2].clear(); 26 ll kk=1; ll num1=0,num2=0; 27 for (int i=1;i<=num;i++){ 28 if (i==1) kk=1ll; else kk=kk*2; 29 if (i%2==1 && a[i]==1) v[1].push_back(kk); 30 if (i%2==0 && a[i]==1) v[2].push_back(kk); 31 } 32 ll ans1,ans2; 33 if(x%3==1){ 34 if(v[1].size()>0){ 35 ans1=x^v[1][0]; 36 if(v[2].size()>0) ans2=v[1][0]^v[2][0]; 37 else ans2=v[1][0]^v[1][1]^v[1][2]; 38 }else{ 39 ans1=x^v[2][0]^v[2][1]; 40 ans2=v[2][0]^v[2][1]^v[2][2]; 41 } 42 }else{ 43 if(v[2].size()>0){ 44 ans1=x^v[2][0]; 45 if(v[1].size()>0) ans2=v[1][0]^v[2][0]; 46 else ans2=v[2][0]^v[2][1]^v[2][2]; 47 }else{ 48 ans1=x^v[1][0]^v[1][1]; 49 ans2=v[1][0]^v[1][1]^v[1][2]; 50 } 51 } 52 printf("2 %lld %lld ",ans1,ans2); 53 } 54 return 0; 55 }
J.free
传送:https://ac.nowcoder.com/acm/contest/884/J
题意:$n$个点,$m$条边,选择$k$条使其长度为0,问$s$到$T$的最短路为多少。
数据范围:$1<=n,m<=10^3,0<=k<=m,1<=l<=10^9$。会有子环与重边。
分析:K条路免费求最短路。
1 #include <queue> 2 #include <algorithm> 3 #include <cstring> 4 #include <cstdio> 5 #define m(a,b) memset(a,b,sizeof a) 6 #define mak(a,b) make_pair(a,b) 7 #define pii pair<int,pair<int,int> > 8 using namespace std; 9 const int N=1e3+5,M=1e3+5; 10 const int INF=0x3f3f3f3f; 11 int tot,K,s,t,n,m; 12 int head[N],d[N][N]; 13 struct Edge{int to,len,nex;}edge[M*2]; 14 priority_queue<pii,vector<pii>,greater<pii> >q; 15 void add(int from,int to,int len) 16 { 17 edge[++tot]=(Edge){to,len,head[from]};head[from]=tot; 18 edge[++tot]=(Edge){from,len,head[to]};head[to]=tot; 19 } 20 void dij() 21 { 22 while(!q.empty()) q.pop(); 23 m(d,INF); 24 q.push(mak(0,mak(s,0))),d[s][0]=0; 25 while(!q.empty()) 26 { 27 int x=q.top().second.first,k=q.top().second.second; 28 q.pop(); 29 for(int i=head[x];i;i=edge[i].nex) 30 { 31 int y=edge[i].to,l=edge[i].len; 32 if(d[x][k]+l<d[y][k]) //该条路不免费 33 { 34 d[y][k]=d[x][k]+l; 35 q.push(mak(d[y][k],mak(y,k))); 36 } 37 if(k+1<=K&&d[x][k]<d[y][k+1]) //该条路免费 38 { 39 d[y][k+1]=d[x][k]; 40 q.push(mak(d[y][k+1],mak(y,k+1))); 41 } 42 } 43 } 44 } 45 int main() 46 { 47 scanf("%d%d%d%d%d",&n,&m,&s,&t,&K); 48 while(m--) 49 { 50 int from,to,len; 51 scanf("%d%d%d",&from,&to,&len); 52 if(from==to) continue; 53 add(from,to,len); 54 } 55 dij(); 56 int ans=INF; 57 for(int i=0;i<=K;i++) //需要遍历一下找到最优解 58 ans=min(ans,d[t][i]); 59 printf("%d ",ans); 60 }
K.number
传送:https://ac.nowcoder.com/acm/contest/884/K
题意:给定一个数字字符串,问有多少个子串是300的倍数。
数据范围:$1<=len<=10^5$。
分析:记录$dp[i][j]$为右端点为$i$的满足$%300=j$的子串的个数。
1 #include<bits/stdc++.h> 2 #define ll long long 3 using namespace std; 4 const int maxn=1e5+7; 5 char a[maxn]; 6 ll num[maxn][310]; 7 int main() 8 { 9 scanf("%s",a); 10 int st=strlen(a); 11 ll ans=0; 12 num[0][a[0]-'0']=1; 13 if(a[0]=='0') ans=1; 14 for (int i=1;i<st;i++) 15 { 16 for (int j=0;j<300;j++) 17 { 18 int x=(j*10+(a[i]-'0'))%300; 19 num[i][x]+=num[i-1][j]; 20 } 21 num[i][a[i]-'0']++;ans+=num[i][0]; 22 } 23 printf("%lld ",ans); 24 return 0; 25 }