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;
}
重要的是自信,一旦有了自信,人就会赢得一切。