2019牛客多校第四场题解

2019牛客多校第四场题解

题目链接

A.meeting

设最远关键点距离为\(d\),那么答案就是\(\lceil\frac{d}{2}\rceil\)
直接换根胡乱dp也行。。

Code
#include <bits/stdc++.h>
using namespace std;
 
const int maxn = 1e5+10;
vector<int>g[maxn];
int vis[maxn];
int len,st,ed;
void dfs(int u,int fa,int dep){
    if(vis[u]){
        if(dep>len){
            len=dep;
            ed=u;
        }
    }
    for(int i=0;i<g[u].size();i++){
        int v=g[u][i];
        if(v==fa)continue;
        dfs(v,u,dep+1);
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n,k;
    cin>>n>>k;
    for(int i=1;i<n;i++){
        int u,v;
        cin>>u>>v;
        g[u].push_back(v),g[v].push_back(u);
    }
    for(int i=1;i<=k;i++){
        cin>>st;
        vis[st]=1;
    }
    len=1,ed=st;
    dfs(st,0,1);
    st=ed;
    dfs(st,0,1);
    cout<<len/2<<endl;
}

B.xor

据说是个线性基交的板子题,但还是有一大波神仙A了。。
求交的话大概就是对于两个基集合\(B1,B2\),枚举\(B2\)中的基,如果与\(B1\)线性无关,那么就插在\(B1\)里面去;否则就对于当前的基,异或掉\(B1\)中之前插进去的\(B2\)的基,然后将其插入交集里面就行了。证明的话可以看看博客:传送门

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned int ui;
const int N = 50005;
int n, m;
struct node{
    ui r[32], f[32];
    bool ins(ui x) {
        for(int i = 31; i >= 0; i--) {
            if(x >> i) {
                if(!r[i]) {r[i] = x; return 1;}
                else x ^= r[i];
            }
        }
        return 0;
    }
    bool ins2(ui x) {
        ui tmp = x;
        for(int i = 31; i >= 0; i--) {
            if(x >> i) {
                if(!r[i]) {r[i] = x; f[i] = tmp; return 1;}
                else {
                    x ^= r[i]; tmp ^= f[i];
                }
            }
        }
        return 0;
    }
    void clear() {
        for(int i = 0; i <= 31; i++) r[i] = f[i] = 0;
    }
    bool find(ui x) {
        for(int i = 31; i >= 0; i--) {
            if(x >> i) {
                if(!r[i]) return 0;
                x ^= r[i];
            }
        }
        return x == 0;
    }
    int calc(ui x) {
        int ans = 0;
        for(int i = 31; i >= 0; i--) {
            if(x >> i) {
                x ^= r[i];
                ans ^= f[i];
            }
        }
        return ans;
    }
};
node _merge(node u, node v) {
    node tmp, res; res.clear();
    tmp = u;
    for(int i = 31; i >= 0; i--) {
        ui x = v.r[i];
        if(tmp.find(x)) {
            res.ins(x ^ tmp.calc(x));
        } else tmp.ins2(x);
    }
    return res;
}
ui a[N][33];
node b[N << 2];
void build(int o, int l, int r) {
    if(l == r) {
        b[o].clear();
        for(int j = 1; j <= 32; j++) b[o].ins(a[l][j]);
        return ;
    }
    int mid = (l + r) >> 1;
    build(o << 1, l, mid); build(o << 1|1, mid + 1, r);
    b[o] = _merge(b[o << 1], b[o << 1|1]);
}
bool query(int o, int l, int r, int L, int R, ui v) {
    if(L <= l && r <= R) {
        return b[o].find(v);
    }
    int mid = (l + r) >> 1;
    bool ans = 1;
    if(L <= mid) ans &= query(o << 1, l, mid, L, R, v);
    if(R > mid) ans &= query(o << 1|1, mid + 1, r, L, R, v) ;
    return ans;
}
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> n >> m;
    for(int i = 1; i <= n; i++) {
        int k; cin >> k;
        for(int j = 1; j <= k; j++) cin >> a[i][j];
    }
    build(1, 1, n);
    for(int i = 1, l, r; i <= m; i++) {
        ui x; cin >> l >> r >> x;
        if(query(1, 1, n, l, r, x)) cout << "YES" << '\n';
        else cout << "NO" << '\n';
    }
    return 0;
}

C.sequence

南昌网络赛出过,单调栈+线段树维护就行。

Code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ld;
const int MAXN = 3e6 + 5, MAXM = 2e4 + 5, BOUND = 2e5 + 5, MOD = 1e9 + 7, INF = 0x3f3f3f3f, base = 10000;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
const double PI = acos(-1.0), eps = 1e-9;
#define mid l + ((r-l)>>1)
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define lc(x) ch[x][0]
#define pii pair<int,int>
#define vi vector<int>
#define RR register int
#define rc(x) ch[x][1]
#define rep(i,a,b) for(RR i=(a);i<=(b);++i)
#define random(a,b) ((a)+rand()%((b)-(a)+1))
struct Istream {
    template <class T>
    Istream &operator >>(T &x) {
        static char ch; static bool neg;
        for (ch = neg = 0; ch < '0' || '9' < ch; neg |= ch == '-', ch = getchar());
        for (x = 0; '0' <= ch && ch <= '9'; (x *= 10) += ch - '0', ch = getchar());
        x = neg ? -x : x;
        return *this;
    }
}fin;
 
struct Ostream {
    template <class T>
    Ostream &operator <<(T x) {
        x < 0 && (putchar('-'), x = -x);
        static char stack[233]; static int top;
        for (top = 0; x; stack[++top] = x % 10 + '0', x /= 10);
        for (top == 0 && (stack[top = 1] = '0'); top; putchar(stack[top--]));
        return *this;
    }
 
    Ostream &operator <<(char ch) {
        putchar(ch);
        return *this;
    }
}fout;
int n, a[MAXN], b[MAXN], l1[MAXN], r1[MAXN], st[MAXN], top = 0;
ll maxv[MAXN << 2][2], minv[MAXN << 2][2], s[MAXN], s2[MAXN];
inline void pushUp(int o) {
    rep(i, 0, 1) {
        maxv[o][i] = max(maxv[o << 1][i], maxv[o << 1 | 1][i]);
        minv[o][i] = min(minv[o << 1][i], minv[o << 1 | 1][i]);
    }
}
void build(int o, int l, int r) {
    if (l == r) {
        maxv[o][0] = minv[o][0] = s[l];
        maxv[o][1] = minv[o][1] = s2[l];
        return;
    }
    int m = mid;
    build(lson); build(rson);
    pushUp(o);
}
ll query(int o, int l, int r, int L, int R, int t, int t2) {
    if (l >= L && r <= R) {
        if (t == 0)return minv[o][t2];
        else return maxv[o][t2];
    }
    int m = mid;
    ll ans;
    if (t == 0) {
        ans = INFL;
        if (L <= m)ans = min(ans, query(lson, L, R, t, t2));
        if (R > m)ans = min(ans, query(rson, L, R, t, t2));
    }
    else {
        ans = -INFL;
        if (L <= m)ans = max(ans, query(lson, L, R, t, t2));
        if (R > m)ans = max(ans, query(rson, L, R, t, t2));
    }
    return ans;
}
int main() {
    //ios::sync_with_stdio(false); cin.tie(0);
    fin >> n;
    rep(i, 1, n)fin >> a[i];
    rep(i, 1, n)fin >> b[i];
    rep(i, 1, n)s[i] = s[i - 1] + b[i];
    for (int i = n; i >= 1; i--)s2[i] = s2[i + 1] + b[i];
    build(1, 0, n + 1);
    // 左边第一个比当前小
    st[0] = 0;
    rep(i, 1, n) {
        while (top &&a[st[top]] >= a[i])top--;
        l1[i] = st[top];
        st[++top] = i;
    }
    st[0] = n + 1;
    top = 0;
    // 右边第一个比当前小
    for (int i = n; i >= 1; i--) {
        while (top &&a[st[top]] >= a[i])top--;
        r1[i] = st[top];
        st[++top] = i;
    }
    ll ans = -INFL;
    rep(i, 1, n) {
        if (a[i] > 0) {
            ll tmp = a[i] * (s[i] - query(1, 0, n + 1, l1[i], i, 0, 0) +
                s2[i] - query(1, 0, n + 1, i, r1[i], 0, 1) - b[i]);
            //printf("%d %lld %lld %lld\n", i, query(1, 0, n + 1, l1[i], i, 0, 0), query(1, 0, n + 1, i, r1[i], 0, 1), (s[i] - query(1, 0, n + 1, l1[i], i, 0, 0) +
            //s2[i] - query(1, 0, n + 1, i, r1[i], 0, 1) - a[i]));
            ans = max(ans, tmp);
        }
        else {
            ll tmp = a[i] * (s[i] - query(1, 0, n + 1, l1[i], i, 1, 0) +
                s2[i] - query(1, 0, n + 1, i, r1[i], 1, 1) - b[i]);
            //printf("%d %lld %lld %lld\n", i, query(1, 0, n + 1, l1[i], i, 0, 0), query(1, 0, n + 1, i, r1[i], 0, 1), (s[i] - query(1, 0, n + 1, l1[i], i, 0, 0) +
            //s2[i] - query(1, 0, n + 1, i, r1[i], 0, 1) - a[i]));
            ans = max(ans, tmp);
        }
    }
    cout << ans << '\n';
    return 0;
}

D.triples I

这个题可以往每个二进制位上面去想,会发现有这样一个式子:\(2^x\)%\(3!=0\),并且对于一个合法的\(a\),其二进制个数不会少于\(3\)
因为模数为3,所以可以分情况讨论。
\(a\)%\(3=0\)时,答案显然为一个;
\(a\)%\(3=1\)时,由上面\(2^x\)%\(3!=0\)可知每个二进制位模\(3\)要么为1,要么为2,而\(a\)是所有二进制位加起来,那么凑出3的倍数也就取决于相关二进制的数量了。
所以就再分类讨论一下:记\(cnt_i\)表示二进制位模3等于\(i\)的个数。之后对这个分类讨论一下就行了。
详见代码吧:

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 64;
ll a;
int T;
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> T;
    while(T--) {
        cin >> a;
        if(a % 3 == 0) {
            cout << 1 << ' ' << a << '\n';
            continue;
        }
        cout << 2 << ' ' ;
        vector <int> v1, v2;
        for(int i = 0; i < N; i++) {
            if(!(a >> i & 1)) continue;
            if((1LL << i) % 3 == 1) v1.push_back(i);
            else v2.push_back(i);
        }
        if(a % 3 == 1) {
            if((int)(v1.size()) >= 2) {
                cout << a - (1LL << v1[0]) << ' ' << a - (1LL << v1[1]) << '\n';
            } else if((int)v1.size() == 1) {
                cout << a - (1LL << v1[0]) << ' ' << (1LL << v1[0]) + (1LL << v2[0]) << '\n';
            } else {
                cout << (1LL << v2[0]) + (1LL << v2[1]) + (1LL << v2[2])<< ' ' << a - (1LL << v2[0]) - (1LL << v2[1]) << '\n';
            }
        } else {
            if((int)(v2.size()) >= 2) {
                cout << a - (1LL << v2[0]) << ' ' << a - (1LL << v2[1]) << '\n';
            } else if((int)v2.size() == 1) {
                cout << a - (1LL << v2[0]) << ' ' << (1LL << v2[0]) + (1LL << v1[0]) << '\n';
            } else {
                cout << (1LL << v1[0]) + (1LL << v1[1]) + (1LL << v1[2])<< ' ' << a - (1LL << v1[0]) - (1LL << v1[1]) << '\n';
            }
        }
    }
    return 0;
}

I.string

对于一个字符串,如果同时存在\(a\)\(rev(a)\),我们只求出本质不同的子串,它们会被算两次,而其余的没有\(rev\)的就会只算一次。
但是这个题目中\(a\)\(rev(a)\)只会被计算一次。但是观察到如果后面拼接一个反串,那么\(a\)\(rev(a)\)同样被计算两次,而其余的也会被计算两次,但回文串除外。
那么我们需要做得就是用后缀数组得到拼接过后本质不同的串,但不算上中间拼接字符,设为\(p\);另外考虑回文串只会被计算一次,所以加上串中回文串的数量\(q\)。那么最终答案就是\(\frac{p+q}{2}\)
代码如下:

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 4e5 + 5;
char s[N];
namespace SA{                                       //sa:1...n  Rank:0...n-1
    int x[N], y[N], sa[N], c[N], height[N], Rank[N];
    int f[N][20], lg[N];
    int n;                                          //length
    void da(char *s, int m){
        n++;
        for(int i = 0; i < m; i++) c[i] = 0;
        for(int i = 0; i < n; i++) c[x[i] = s[i]]++;
        for(int i = 1; i < m; i++) c[i] += c[i - 1] ;
        for(int i = n - 1; i >= 0; i--) sa[--c[x[i]]] = i;
        for(int k = 1; k <= n; k <<= 1) {
            int p = 0 ;
            for(int i = n - k; i < n; i++) y[p++] = i ;
            for(int i = 0; i < n; i++) if(sa[i] >= k) y[p++] =sa[i] - k;
            for(int i = 0; i < m; i++) c[i] = 0;
            for(int i = 0; i < n; i++) c[x[y[i]]]++;
            for(int i = 1; i < m; i++) c[i] += c[i - 1];
            for(int i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i] ;
            swap(x , y); p = 1; x[sa[0]] = 0;
            for(int i = 1; i < n; i++)
                x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i-1] + k] == y[sa[i] + k] ? p - 1 : p++;
            if(p >= n) break ;
            m = p;
        }
        n--;
        int k = 0;
        for(int i = 0; i <= n; i++) Rank[sa[i]] = i;
        for(int i = 0; i < n; i++) {
            if(k) k--;
            int j = sa[Rank[i] - 1];
            while(s[i + k] == s[j + k]) k++;
            height[Rank[i]] = k;
        }
    }
    ll count() {
        ll ans = 0;
        for(int i = 1; i <= n; i++) ans += n - sa[i] - height[i];
        return ans;
    }
    void init() {
        for(int i = 2; i < N; i++) lg[i] = lg[i >> 1] + 1;
        for(int i = 2; i <= n; i++) f[i][0] = height[i];
        for(int j = 1; j < 20; j++)
            for(int i = 2; i + (1 << j) - 1 <= n; i++)
                f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]) ;
    }
    int get_lcp(int l, int r) {
        if(Rank[l] > Rank[r]) swap(l, r);
        l = Rank[l] + 1, r = Rank[r];
        int k = lg[r - l + 1];
        return min(f[l][k], f[r - (1 << k) + 1][k]);
    }
}
namespace PAM{
    int ch[N][26], fail[N], len[N], st[N], cnt[N];
    int sz, n, last;
    int New(int l, int f) {
        memset(ch[++sz], 0, sizeof(ch[sz]));
        len[sz] = l, fail[sz] = f;
        return sz;
    }
    void init() {
        sz = -1;
        New(0, 1); last = New(-1, 0);
        st[n = 0] = -1;
        memset(cnt, 0, sizeof(cnt));
    }
    int getf(int x) {
        while(st[n - len[x] - 1] != st[n]) x = fail[x];
        return x;
    }
    bool Insert(int c) { //int
        st[++n] = c;
        int x = getf(last);
        bool F = 0;
        if(!ch[x][c]) {
            F = 1;
            int f = getf(fail[x]);
            ch[x][c] = New(len[x] + 2, ch[f][c]);
        }
        last = ch[x][c];
        cnt[last] = 1;
        return F;
    }
    void count() {
        for(int i = sz; i >= 1; i--) cnt[fail[i]] += cnt[i];
    }
};
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> s;
    int n = strlen(s);
    ll ans = 0;
    PAM::init();
    for(int i = 0; i < n; i++) PAM::Insert(s[i] - 'a');
    for(int i = 1; i <= PAM::sz; i++) ans += PAM::cnt[i];
    s[n] = '&';
    for(int i = n + 1; i <= 2 * n; i++) s[i] = s[2 * n - i];
    s[n * 2 + 1] = '\0';
    SA::n = 2 * n + 1;
    SA::da(s, 520);
    ans += SA::count();
    ans -= 1ll * (n + 1) * (n + 1);
    cout << ans / 2;
    return 0;
}

J.free

直接二维状态的dijkstra跑一下就行,转移的时候就类似于dp那样,考虑当前这边是否免费。考场上我zz了枚举了一下,还好数据较小,不然GG。

Code
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int N = 1e3 + 5;
int n, m, S, T, k;
struct edge{
    int u, v, w;
    bool operator < (const edge &A)const {
        return w < A.w;
    }
}E[N];
struct Edge{
    int u,v,w,next ;
}e[N << 1];
int tot, head[N];
int W[N][N];
struct node{
    int d, u, c;
    bool operator < (const node &A)const{
        return d>A.d;
    }
};
void adde(int u,int v,int w){
    e[tot].v=v;e[tot].w=w;e[tot].next=head[u];head[u]=tot++;
}
int d[N];
bool vis[N];
bool Dijkstra(int s){
    priority_queue <node> q; memset(d,INF,sizeof(d));
    memset(vis,0,sizeof(vis));d[s]=0;
    q.push(node{0, s, 0});
    while(!q.empty()){
        node cur = q.top();q.pop();
        int u=cur.u;
        vis[u]=1;
        for(int i=head[u];i!=-1;i=e[i].next){
            int v=e[i].v;
            if(d[v]>d[u]+e[i].w && cur.c + (e[i].w == 0) <= k){
                d[v]=d[u]+e[i].w;
                q.push(node{d[v],v, cur.c + (e[i].w == 0)});
            }
        }
    }
    return d[T] != INF;
}
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> n >> m >> S >> T >> k;
    int tmp = 0;
    for(int i = 1; i <= m; i++) {
        int u, v, w; cin >> u >> v >> w;
        if(u == v) continue;
        if(u > v) swap(u, v);
        if(W[u][v]) W[u][v] = min(W[u][v], w);
        else W[u][v] = w;
    }
    for(int i = 1; i <= n; i++)
        for(int j = i + 1; j <= n; j++)
            if(W[i][j]) E[++tmp] = edge{i, j ,W[i][j]};
    m = tmp;
    sort(E + 1, E + m + 1);
    int ans = INF;
    for(int i = m; i >= 1; i--) {
        memset(head, -1, sizeof(head)); tot = 0;
        for(int j = i; j <= m; j++) {
            adde(E[j].u, E[j].v, 0);
            adde(E[j].v, E[j].u, 0);
        }
        for(int j = 1; j < i; j++) {
            adde(E[j].u, E[j].v, E[j].w);
            adde(E[j].v, E[j].u, E[j].w);
        }
        if(Dijkstra(S)) ans = min(ans, d[T]);
    }
    cout << ans << '\n';
    return 0;
}

K.number

签到题,dp或者维护前缀模3的值都可以做。

Code
#include<bits/stdc++.h>
typedef long long ll;
const int MAXN = 1e5 + 5, MAXM = 1e5 + 5, INF = 0x3f3f3f3f, MOD = 998244353;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
using namespace std;
const int oo = (1e9) - (1e6);
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define mid l + ((r-l)>>1)
#define pb push_back
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define all(v) (v.begin(),v.end())
typedef long double db;
 
char s[MAXN];
int n, cnt[MAXN], pre[MAXN];
int main() {
    cin >> (s + 1);
    n = strlen(s + 1);
    int sum = 0;
    ll ans = 0;
    for (int i = 1; i <= n; i++) {
        sum = (sum * 10 + s[i] - '0') % 300;
        if ((sum - pre[i - 1] * 10 % 300 + 300) % 300 == 0)ans++;
        if (sum == 100)ans += cnt[1];
        else if (sum == 200)ans += cnt[2];
        else if (sum == 0)ans += cnt[0];
        if (i - 1 >= 0)cnt[pre[i - 1] % 3]++;
        pre[i] = sum;
    }
    cout << ans << '\n';
    return 0;
}
posted @ 2019-07-30 14:21  heyuhhh  阅读(296)  评论(0编辑  收藏  举报