多校补题

第一场
09String
感觉就是银行家算法。。。。赛时没看不过看了也不一定能出,因为很多细节要考虑。
思路就是对于每一位贪心的试探,验证一下放进之后能不能还能构成答案,,如果能的话就放进去,不能就撤销。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;
typedef long long ll;
const ll mod = 1e9+7;
int Case = 1;
int n, m;
struct node{
    int l, r;
}s[30];
char ss[maxn], res[maxn];
pair<vector<int>, int>ch[30];
int num[maxn][30], use[30];
void init() {
    for(int i = 0; i <= 26; i++) {
        ch[i].first.clear();
        ch[i].second = 0;
        use[i] = 0;
    }
    for(int i = 0; i <= n+1; i++) {
        for(int j = 0; j <= 26; j++) num[i][j] = 0;
    }
}
void solve() {
    int k;scanf("%d",  &k);
    n = strlen(ss+1);
    init();int last = 0;
    for(int i = 0; i <= 26; i++)
        {scanf("%d%d", &s[i].l, &s[i].r);}
    for(int i = 1; i <= n; i++)
        ch[ss[i]-'a'].first.push_back(i);
    for(int i = n; i >= 1; i--)
        for(int j = 0; j <= 25; j++)
            if(j == ss[i]-'a') num[i][j] = num[i+1][j]+1;
            else num[i][j] = num[i+1][j];

    for(int i = 1; i <= k; i++) {
        int ff = 0;
        for(int j = 0; j <= 25; j++) {
            if(use[j] == s[j].r) continue;
            while(ch[j].second < ch[j].first.size() && ch[j].first[ch[j].second] <= last) ch[j].second++;
            if(ch[j].second == ch[j].first.size()) continue;
            int pos = ch[j].first[ch[j].second];
            use[j]++;int flag = 0, sum = 0;
            for(int g = 0; g <= 25; g++) {
                if(num[pos+1][g]+use[g] < s[g].l) {flag = 1;break;}
                sum += max(0, s[g].l-use[g]);
            }
            if(sum > (k-i)) flag = 1;
            sum = 0;
            for(int g = 0; g <= 25; g++) {
                sum += min(s[g].r-use[g], num[pos+1][g]);
            }
            if(sum < (k-i)) flag = 1;
            if(flag) use[j]--;
            else {
                last = pos;
                res[i] = j+'a';
                ff = 1;
                break;
            }
        }
        if(!ff) {printf("-1\n"); return;}
    }
    for(int i = 1; i <= k; i++) putchar(res[i]);
    puts("");
    return;
}
int main() {
    //g++ -std=c++11 -o2 1.cpp -o f && ./f < in.txt
    //ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    //freopen("out.txt","w",stdout);
#endif
while(scanf("%s", ss+1) == 1) {
    solve();
    }
return 0;
}

第二场
占坑,待补
02 Beauty Of Unimodal Sequence
09 I Love Palindrome String
听说是回文树裸题。。就去学了一下回文树。。还真是裸题。。
在回文树维护的本质不同的回文串中统计l-mid也是回文串的个数,其实就是在找回文串是否前一半和后一半是否相同。
看图就应该很好懂。
在这里插入图片描述

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 3e5+5;
typedef long long ll;
const ll mod = 1e9+7;
const int p = 1333;
int Case = 1;
int ret[maxn];
unsigned long long P[maxn], _hash[maxn];
unsigned long long gethash(int l, int r) {
    return _hash[r]-_hash[l-1]*P[(r-l+1)];
}
bool check(int l, int r) {
    int mid = (l+r)/2;
    if((r-l+1)&1) return gethash(l, mid) == gethash(mid, r);
    else return gethash(l, mid) == gethash(mid+1, r);
}
struct pam{
    int S[maxn];
    int len[maxn], fail[maxn], last, n;
    int ch[maxn][30], cnt[maxn], tot, id[maxn];
    void init() {
        tot = 0;
        newnode(0);newnode(-1);last = 0;
        last = 0;n = 0;S[n] = -1;fail[0] = 1;
    }
    int newnode(int x) {
        len[tot] = x;
        cnt[tot] = fail[tot] = 0;
        for(int i = 0; i < 26; i++) ch[tot][i] = 0;
        return tot++;
    }
    int getfail(int x) {
        while(S[n] != S[n-len[x]-1]) x = fail[x];
        return x;
    }
    void insert(char *s) {
        int l = strlen(s);
        for(int i = 0; i < l; i++) {
            int c = s[i]-'a';
            S[++n] = c;
            int cur = getfail(last);
            if(!ch[cur][c]) {
                int now = newnode(len[cur]+2);
                fail[now] = ch[getfail(fail[cur])][c];
                ch[cur][c] = now;
            }
            last = ch[cur][c];cnt[last]++;id[last] = n;
        }
    }
    void count() {
        for(int i = tot-1; i >= 0; i--) cnt[fail[i]] += cnt[i];
    }
    void cal() {
        for(int i = 0; i < tot; i++) {
            if(len[i] > 0) {
                ret[len[i]] += check(id[i]-len[i], id[i]-1)*cnt[i];
            }
        }
    }
}pam;
int n, m;
char ss[maxn];
void solve() {
    memset(ret, 0, sizeof(ret));
    pam.init();
    pam.insert(ss);n = strlen(ss);
    _hash[0] = ss[0];
    for(int i = 1; i < n; i++) {
        _hash[i] = _hash[i-1]*p+ss[i];
    }
    pam.count();pam.cal();
    for(int i = 1; i <= n; i++) {
        printf("%d%c", ret[i], i==n?'\n':' ');
    }
    return;
}
int main() {
    //g++ -std=c++11 -o2 1.cpp -o f && ./f < in.txt
    //ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    //freopen("out.txt","w",stdout);
#endif
P[0] = 1;
for(int i = 1; i < maxn; i++) {
    P[i] = P[i-1]*p;
}
while(scanf("%s", ss) == 1) {
    solve();
    }
return 0;
}

12 Longest Subarray
听说可以for()for()剪枝过。。。
正解思路:枚举右端点,当右端点移动。设位置为i,元素为x,那么元素x对1-n这个区间的影响分为两个区间:x数量为0的区间,x数量大于等于k的区间。设距离i最近的x位置为p1, 距离i的第k个的x位置为pk,那么就是【p1+1,i】,【1, pk】。
线段树初始化每个叶子节点的权值为C,当右端点移动,把[p1+1, i]区间减一,然后把第k个+1到第k+1的区间加一,询问答案就是左边第一个等于C的位置。有点巧妙的套路。。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
typedef long long ll;
const ll mod = 1e9+7;
int Case = 1;
int n, m, C;
int mx[maxn<<2], lazy[maxn<<2], pos[maxn<<2];
void pushup(int rt) {
    mx[rt] = max(mx[rt<<1], mx[rt<<1|1]);
    pos[rt] = mx[rt] == mx[rt<<1] ? pos[rt<<1]:pos[rt<<1|1];
}
void build(int rt, int l, int r) {
    mx[rt] = lazy[rt] = pos[rt] = 0;
    if(l == r) {
        mx[rt] = C;
        pos[rt] = l;
        return;
    }
    int mid = (l+r)/2;
    build(rt<<1, l, mid);
    build(rt<<1|1, mid+1, r);
    pushup(rt);
}
void pushdown(int rt) {
    if(lazy[rt]) {
        int &x = lazy[rt];
        lazy[rt<<1] += x;lazy[rt<<1|1] += x;
        mx[rt<<1] += x;mx[rt<<1|1] += x;
        x = 0;
    }
}
void update(int rt, int L, int R, int l, int r, int c) {
    if(l > r)return;
    if(l == L && r == R) {
        mx[rt] += c;
        lazy[rt] += c;
        return;
    }
    pushdown(rt);
     int mid = (R+L)/2;
     if(r <= mid) update(rt<<1, L, mid, l, r, c);
     else if(l > mid) update(rt<<1|1, mid+1, R, l, r, c);
     else update(rt<<1, L, mid, l, mid, c), update(rt<<1|1, mid+1, R, mid+1, r, c);
     pushup(rt);
}
int query(int rt, int L, int R, int l, int r) {
    if(l > r || mx[rt] !=  C) return 0;
    if(l == L && R == r) return pos[rt];
    int mid = (L+R)/2;
    pushdown(rt);
    if(r <= mid) return query(rt<<1, L, mid, l, r);
    else if(l > mid) return query(rt<<1|1, mid+1, R, l, r);
    else {
        int t;
        return (t = query(rt<<1, L, mid, l, mid))?t:query(rt<<1|1, mid+1, R, mid+1, r);
    }
}
vector<int>ve[maxn];
int k;
void solve() {
    int res = 0;
    build(1, 1, n);
    for(int i = 1; i <= n; i++) ve[i].clear(), ve[i].push_back(0);
    for(int i = 1; i <= n; i++) {
        int x;scanf("%d", &x);
        if(ve[x].back()+1 <= i) update(1, 1, n, ve[x].back()+1, i, -1);
        ve[x].push_back(i);
        int p = ve[x].size()-k-1;
        if(p >= 0) update(1, 1, n, ve[x][p]+1, ve[x][p+1], 1);
        int q = query(1, 1, n, 1, i);
        if(q) res = max(res, i-q+1);
    }
    printf("%d\n", res);
    return;
}
int main() {
    //g++ -std=c++11 -o2 1.cpp -o f && ./f < in.txt
    //ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    //freopen("out.txt","w",stdout);
#endif
while(scanf("%d%d%d", &n, &C, &k) == 3) {
    solve();
    }
return 0;
}

第三场
04Distribution of books
考虑二分内DP,dp[i]表示前i个数最多能分成多少块。状态转移就是 d p [ i ] = m a x ( d p [ j ] ) + 1 ( s u m [ i ] s u m [ j ] &lt; = x ) dp[i] = max(dp[j])+1(sum[i]-sum[j] &lt;= x)
把前缀和的dp值丢线段树里,维护一下区间最值就行了。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5+5;
typedef long long ll;
const ll mod = 1e9+7;
int Case = 1;
int n, m;
int mx[maxn<<4];
void pushup(int rt) {
    mx[rt] = max(mx[rt<<1], mx[rt<<1|1]);
}
void build(int rt, int l, int r) {
    mx[rt] = -2000000000;
    if(l == r) return;
    int mid = (l+r)/2;
    build(rt<<1, l, mid);build(rt<<1|1, mid+1, r);
}
void update(int rt, int l, int r, int pos, int val) {
    if(l > r) return;
    if(l == r) {
        mx[rt] = val;return;
    }
    int mid = (l+r)/2;
    if(pos <= mid) update(rt<<1, l, mid, pos, val);
    else update(rt<<1|1, mid+1, r, pos, val);
    pushup(rt);
}
int query(int rt, int L, int R, int l, int r) {
    if(l > r) return 0;
    if(l == L && R == r) {
        return mx[rt];
    }
    int mid = (L+R)/2;
    if(r <= mid) return query(rt<<1, L, mid, l, r);
    else if(l > mid) return query(rt<<1|1, mid+1, R, l, r);
    else return max(query(rt<<1, L, mid, l, mid), query(rt<<1|1, mid+1, R, mid+1, r));
}
int cnt[maxn], a[maxn];
ll pre[maxn];
int pos[maxn];
vector<ll>ve;
int getid(ll x) {
    return lower_bound(ve.begin(), ve.end(), x) - ve.begin()+1;
}
bool cal(ll x) {
    build(1, 1, n+1);update(1, 1, n+1, pos[0], 0);
    for(int i = 1; i <= n; i++) {
        int p = getid(pre[i]-x);
        update(1, 1, n+1, pos[i], query(1, 1, n+1, p, n+1)+1);
    }
    return mx[1] >= m;
}
void solve() {
    ve.clear();memset(cnt, 0, sizeof(cnt));
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
    ve.push_back(0);
    for(int i = 1; i <= n; i++) pre[i] = pre[i-1] + a[i], ve.push_back(pre[i]);
    sort(ve.begin(), ve.end());
    ve.erase(unique(ve.begin(), ve.end()), ve.end());
    for(int i = 0; i <= n; i++) {
        int x = getid(pre[i]);
        pos[i] = x + cnt[x];cnt[x]++;
    }
    ll l = -2e14, r = 2e14;
    while(r-l > 1ll) {
        ll mid = (l+r)/2;
        if(cal(mid)) r = mid;
        else l = mid;
    }
    if(cal(r)) l++;
    printf("%lld\n", l);
    return;
}
int main() {
    //g++ -std=c++11 -o2 1.cpp -o f && ./f < in.txt
    //ios::sync_with_stdio(false);
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt","w",stdout);
#endif
    scanf("%d", &Case);
    while(Case--) {
        solve();
        }
    return 0;
}

第四场
08K-th Closest Distance
哭了。。赛场上是暴力的用主席树把p的前k个和后k个放进vector里面然后sort,T了一下午。。。
被这个k给骗了。。
可以二分答案,对于mid,查询[p-mid, p+mid]区间中的数是否大于等于k,这玩意显然具有单调性。。。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e6+5;
const int mod = 1e9+9;
int Case = 1;
int n, m, root[maxn], tot;
int ll[maxn<<5], rr[maxn<<5], sum[maxn<<5];
void pushup(int rt) {
    sum[rt] = sum[ll[rt]] + sum[rr[rt]];
}
void build(int &rt, int l, int r) {
    rt = ++tot;
    if(l == r) {sum[rt] = 0;return;}
    int mid = (l + r)/2;
    build(ll[rt], l, mid);
    build(rr[rt], mid+1, r);
    pushup(rt);
}
void insert(int &rt, int pr, int pos, int l, int r) {
    rt = ++tot;
    ll[rt] = ll[pr];rr[rt] = rr[pr];
    sum[rt] = sum[pr];
    if(l == r) {
        sum[rt]++;
        return;
    }
    int mid = (l + r)/2;
    if(pos <= mid) insert(ll[rt], ll[pr], pos, l, mid);
    else insert(rr[rt], rr[pr], pos, mid+1, r);
    pushup(rt);
}
int query(int lc, int rc, int l, int r, int L, int R) {
    if(L > R) return 0;
    if(l == L && R == r) return sum[rc]-sum[lc];
    int mid = (l+r)/2;
    if(R <= mid) return query(ll[lc], ll[rc], l, mid, L, R);
    else if(L > mid) return query(rr[lc], rr[rc], mid+1, r, L, R);
    else return query(ll[lc], ll[rc], l, mid, L, mid) + query(rr[lc], rr[rc], mid+1, r, mid+1, R);
}
int k;
int cc[maxn];
vector<int>ve;
int getid(int x) {
    return lower_bound(ve.begin(), ve.end(), x)-ve.begin()+1;
}
int cal(int L, int R, int p) {
    int l = -1, r = 2e6;
    while(r-l>1) {
        int mid = (r+l)/2;
        int lc = getid(p-mid), num;
        int rc = upper_bound(ve.begin(), ve.end(), p+mid)-ve.begin();
        //assert(lc <= rc);
        num = query(root[L-1], root[R], 1, n, lc, rc);
        if(num >= k) r = mid;
        else l = mid;
    }
    return r;
}
void solve() {
    tot = 0;ve.clear();
    scanf("%d%d", &n, &m);
    build(root[0], 1, n);
    for(int i = 1; i <= n; i++) {
        scanf("%d", &cc[i]);
        ve.push_back(cc[i]);
    }
    sort(ve.begin(), ve.end());
    for(int i = 1; i <= n; i++) {
        insert(root[i], root[i-1], getid(cc[i]), 1, n);
    }
    int last = 0;
    for(int i = 1; i <= m; i++) {
        int l, r, P;
        scanf("%d%d%d%d", &l, &r, &P, &k);
        l ^= last;r ^= last;k ^= last;P ^= last;
        if(l > r) swap(l, r);
        printf("%d\n", last = cal(l, r, P));
    }
    //cout<<"!!!"<<endl;
    return;
}

int main() {
    //freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
    scanf("%d", &Case);
    //scanf("%d", &Case);
    while(Case--) {
        solve();
    }
    return 0;
}

第五场
04equation
把每对a,b看成以-b/a对称的直线,题目转化为这些直线叠加之后是否穿过C。细节很多,具体看代码。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
#define ft first
#define sd second
#define all(c) ((c).begin()), ((c).end())
#define mp(a, b) make_pair(a, b)
#define pb(x) push_back(x)
template<typename T>T gcd(T a, T b) {return b==0?a:gcd(b, a%b);}
#ifndef ONLINE_JUDGE
#define debug(fmt, ...) {printf("debug ");printf(fmt,##__VA_ARGS__);puts("");}
#else
#define debug(fmt, ...)
#endif
const int maxn = 1e5+5;
typedef long long ll;
const ll mod = 1e9+7;
int Case = 1;
int n, C, m;
struct dk{
    ll a, b;
    dk(ll _a = 0, ll _b = 0):a(_a), b(_b) {
        if(a < 0) {a = -a;b = -b;}
    }
    void gao() {ll d = gcd(a, abs(b)); a /= d; b /= d;}
    bool operator<(const dk s)const{
        return b*s.a < s.b*a;
    }
    bool operator<=(const dk s)const{
        return b*s.a <= s.b*a;
    }
    bool operator==(const dk s)const{
        return a*s.b == s.a*b;
    }
}cc[maxn];
vector<dk>res;
void solve() {
    cin>>n>>C;res.clear();
    ll sa = 0, sb = 0;
    for(int i  = 1; i <= n; i++) {
        cin>>cc[i].a>>cc[i].b;
        cc[i].b = -cc[i].b;
        if(cc[i].a < 0) {
            cc[i].a = -cc[i].a;
            cc[i].b = -cc[i].b;
        }
        sa += cc[i].a;sb += cc[i].b;
    }
    sort(cc+1, cc+1+n);
    {dk t(sa, C+sb);if(cc[n] <= t) res.pb(t);}
    for(int i = n; i >= 1; i--) {
        sa -= 2*cc[i].a;sb -= 2*cc[i].b;
        if(!sa) {
            if(C+sb == 0) {
                if(cc[i-1] < cc[i]) {
                    printf("-1\n");
                    return;
                }
            }
            continue;
        }
        dk t(sa, C+sb);
        if(i != 1) {
            if(cc[i-1] <= t && t <= cc[i]) {
                res.pb(t);
            }
        }
        else if(t <= cc[1]){res.pb(t);}
    }
    for(int i = 0; i < res.size(); i++) res[i].gao();
    sort(res.begin(), res.end());
    res.erase(unique(res.begin(), res.end()), res.end());
    printf("%d", (int)res.size());
    for(int i = 0; i < res.size(); i++) printf(" %lld/%lld", res[i].b, res[i].a);
    puts("");
    return;
}
int main() {
    ios::sync_with_stdio(false);cin.tie(0);std::cout.tie(0);
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    //freopen("out1.txt","w",stdout);
#endif
    //scanf("%d", &Case);
    cin>>Case;
    while(Case--) {
        solve();
    }
    return 0;
}

05permutation 1
菜到不能呼吸,,没想到还能dfs。。。太蠢了。。。

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
#define ft first
#define sd second
#define all(c) ((c).begin()), ((c).end())
#define mp(a, b) make_pair(a, b)
#define pb(x) push_back(x)
const int maxn = 105+5;
typedef long long ll;
const ll mod = 1e9+7;
int Case = 1;
int n, m;
int v[maxn], a[maxn];
bool dfs(int p, int l, int r) {
    if(p == n+1) {
        m--;
        if(!m) {
            for(int i = 1; i <= n; i++) printf("%d%c", a[i]-l+1, i==n?'\n':' ');
            return true;
        }
        return false;
    }
    for(int i = r-n+1; i <= l+n-1; i++) {
        if(v[i]) continue;
        v[i] = 1;
        a[p] = i;
        if(dfs(p+1, min(l, i), max(r, i))){
            v[i] = 0;
            return 1;
        }
        v[i] = 0;
    }
    return 0;
}
void solve() {
    cin>>n>>m;
    a[1] = n;
    v[n] = 1;
    dfs(2, n, n);
    v[n] = 0;
    return;
}
int main() {
    ios::sync_with_stdio(false);cin.tie(0);std::cout.tie(0);
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    //freopen("out.txt","w",stdout);
#endif
    //scanf("%d", &Case);
    cin>>Case;
    while(Case--) {
        solve();
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39921637/article/details/97299095
今日推荐