zoukankan      html  css  js  c++  java
  • 2019牛客多校第四场

    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 }
    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
    ");   //可插入,不可构成 
    93         else printf("YES
    ");
    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}) 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 }
    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
    ",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 }
    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
    ",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
    ",ans);
    24     return 0;
    25 }
    K
  • 相关阅读:
    Bzoj 1537: [POI2005]Aut- The Bus 题解 [由暴力到正解]
    Bzoj 3126[Usaco2013 Open]Photo 题解
    Bzoj 3165 [Heoi2013]Segment题解
    Bzoj 2733: [HNOI2012]永无乡 数组Splay+启发式合并
    赛前集训前的总结(警醒)
    bzoj3316 JC loves Mkk题解
    9.22考试 crf的军训 题解
    Luogu3521 [POI2011]ROT-Tree Rotations
    CTSC2012 熟悉的文章
    UVA11468 Substring
  • 原文地址:https://www.cnblogs.com/changer-qyz/p/11261681.html
Copyright © 2011-2022 走看看