2019牛客多校第四场

A.meeting

传送:https://ac.nowcoder.com/acm/contest/884/A

题意:有$n$个结点的树,$k$个人在一些结点上,问他们到某一点集合,问某一个人走的最长的距离为多少。

数据范围:$1<=n<=10^5$。

分析:考虑有人的叶子结点最远的那个就好。把没有人的叶子节点去掉,然后求一个树的直径。答案是$\lceil d/2 \rceil$。

 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\n");
48     else
49     {
50         int T=bfs(S);S=bfs(T);
51         int ans=(dis[S]+1)/2;
52         printf("%d\n",ans);
53     }
54     return 0;
55 }
A

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\n");   //可插入,不可构成 
93         else printf("YES\n");
94     }
95     return 0;
96 }
B

C.sequence

传送:https://ac.nowcoder.com/acm/contest/884/C

题意:给定两个数组$a_i,b_i$。求解:${max}_{1<=l<=r<=n}{min(a_{l..r}) \times sum(b_{l..r}}$。

数据范围:$1<=n<=3\times 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\n",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 }
C

 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\n",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\n",ans1,ans2);
53     }
54     return 0;
55 }
D

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\n",ans);
60 }
J

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\n",ans);
24     return 0;
25 }
K

 

posted @ 2019-07-29 00:45  Changer-qyz  阅读(198)  评论(0编辑  收藏  举报