十一月刷题记录

2019CCPC哈尔滨 L. LRU Algorithm(字符串)

做法1:Trie

//
// Created by Artist on 2021/10/29.
//

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
#define all(x) x.begin(),x.end()
#define DB1(args...) do { cout << #args << " : "; dbg(args); } while (0)
#define endl '\n'

void dbg() { std::cout << "  #\n"; }

template<typename T, typename...Args>
void dbg(T a, Args...args) {
    std::cout << a << ' ';
    dbg(args...);
}

void io() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); }
const int maxn = 5004;
const int N = 2e6+5;
int a[maxn];
int b[maxn];
int cache[maxn],lst[maxn];
struct Trie {
    unordered_map<int,int> next[N];
    int vis[N];
    int cnt;
    void init() {
        cnt=0;
        next[0].clear();
    }
    int newnode() {
        vis[++cnt]=0;
        next[cnt].clear();
        return cnt;
    }
    int insert(int len) {
        int cur=0;
        for(int i=1;i<=len;++i) {
            if(!next[cur].count(b[i])) next[cur][b[i]]=newnode();
            cur = next[cur][b[i]];
        }
        return cur;
    }
    void query(int len) {
        int cur=0;
        for(int i=1;i<=len;++i) {
            if(!next[cur].count(cache[i])) break;
            cur = next[cur][cache[i]];
            vis[cur]=1;
        }
        while(next[cur].count(0)) {
            cur = next[cur][0];
            vis[cur]=1;
        }
    }
}trie;

signed main() {
    io();
    int _;cin>>_;
    while(_--) {
        int n,q;cin>>n>>q;
        trie.init();
        for(int i=1;i<=n;++i) {
            cin>>a[i];
        }
        for(int i=1;i<=q;++i) {
            int tmp;cin>>tmp;
            for(int j=1;j<=tmp;++j) {
                cin>>b[j];
            }
            lst[i] = trie.insert(tmp);
        }
        int cnt=0;
        for(int i=1;i<=n;++i) {
            int pos=-1;
            for(int j=1;j<=cnt;++j) {
                if(a[i]==cache[j]) {
                    pos=j;
                    break;
                }
            }
            if(pos==-1) {
                for(int j=cnt;j;--j) {
                    cache[j+1]=cache[j];
                }
                cnt++;
                cache[1] = a[i];
            } else {
                for(int j=pos-1;j;--j) {
                    cache[j+1]=cache[j];
                }
                cache[1] = a[i];
            }
            trie.query(cnt);
        }
        for(int i=1;i<=q;++i) {
            if(trie.vis[lst[i]]) {
                cout<<"Yes"<<endl;
            } else {
                cout<<"No"<<endl;
            }
        }
    }
}

做法2:哈希

//
// Created by Artist on 2021/10/29.
//
// 哈希版本
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
#define all(x) x.begin(),x.end()
#define DB1(args...) do { cout << #args << " : "; dbg(args); } while (0)
#define endl '\n'

void dbg() { std::cout << "  #\n"; }

template<typename T, typename...Args>
void dbg(T a, Args...args) {
    std::cout << a << ' ';
    dbg(args...);
}

void io() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); }
const int maxn = 5e3+3;
const int maxq = 2e3+3;
//const int N = 2e6+4;
int a[maxn];
vector<int> query[maxq];
int cache[maxn],len[maxn];
typedef unsigned long long ull;
ull seed=31,base[maxn],_hash[maxn],value[maxq];
void init() {
    base[0]=1;
    for(int i=1;i<maxn;++i)
        base[i]=base[i-1]*seed;
}

ull gethash(int i,int l) {
    return _hash[i+l-1]-_hash[i-1]*base[l];
}
int vis[maxq];

signed main() {
    io();
    int t;cin>>t;
    init();
    while(t--) {
        int n,q;cin>>n>>q;
        for(int i=1;i<=q;++i) {
            query[i].clear();
            vis[i]=0;
            value[i]=0;
        }
        for(int i=1;i<=n;++i) {
            cin>>a[i];
        }
        for(int i=1;i<=q;++i) {
            cin>>len[i];
            for(int j=1;j<=len[i];++j) {
                int tmp;cin>>tmp;
                if(tmp) query[i].pb(tmp);
            }
            len[i]=query[i].size();
            for(int j=0;j<len[i];++j) {
                value[i] = value[i]*seed + query[i][j];
            }
        }
        int cnt=0;
        for(int i=1;i<=n;++i) {
            int pos=-1;
            for(int j=1;j<=cnt;++j) {
                if(cache[j]==a[i]) {
                    pos=j;
                    break;
                }
            }
            if(pos==-1) {
                for(int j=cnt;j;--j) {
                    cache[j+1]=cache[j];
                }
                cache[1] = a[i];
                cnt++;
            } else {
                for(int j=pos-1;j;--j) {
                    cache[j+1]=cache[j];
                }
                cache[1] = a[i];
            }
            for(int j=1;j<=cnt;++j) {
                _hash[j]=_hash[j-1]*seed+cache[j];
            }
            for(int j=1;j<=q;++j) {
                if(gethash(1,len[j])==value[j]) vis[j]=1;
            }
        }
        for(int i=1;i<=q;++i) {
            if(vis[i]) cout<<"Yes"<<endl;
            else cout<<"No"<<endl;
        }
    }
}

2018-19ICPC焦作 H. Can You Solve the Harder Problem?(后缀数组)

题意:给一个数组,求不同连续子序列的最大值的和。
思路:不同连续子序列,考虑后缀数组。首先假设不管是否相同,直接求连续子序列最大值的和,怎么求。枚举区间的左端点,设以 i i i为左端点的所有区间的最大值的和为 d p [ i ] dp[i] dp[i]。有 d p [ i ] = d p [ j ] + a [ i ] ∗ ( j − i ) dp[i]=dp[j]+a[i]*(j-i) dp[i]=dp[j]+a[i](ji),其中 j j j i i i右边第一个值大于 i i i的位置。这个位置(设为 r i g [ i ] rig[i] rig[i])可以通过单调栈求。 d p dp dp数组可以通过从右往左扫求。
现在考虑怎么处理相同连续子序列不重复计算的问题。
我们知道一个后缀的height值表示这个前缀(子串)在前面的后缀中出现过。那么对于这个后缀中的子串,我们只统计长度大于height值的前缀的贡献即可。
i i i为左端点的贡献为 a [ j ] ∗ ( r i g [ j ] − ( i + h e i g h t − 1 ) − 1 ) + d p [ r i g [ j ] ] a[j]*(rig[j]-(i+height-1)-1)+dp[rig[j]] a[j](rig[j](i+height1)1)+dp[rig[j]],其中 j j j [ i , i + h e i g h t − 1 ] [i,i+height-1] [i,i+height1]区间的最大值的位置,可以通过线段树求(因为是静态的,也可以用ST表)。
因为求 h e i g h t height height数组的板子有点初始化的问题导致调了一个小时
在这里插入图片描述

//
// Created by Artist on 2021/10/30.
//

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
#define all(x) x.begin(),x.end()
#define DB1(args...) do { cout << #args << " : "; dbg(args); } while (0)
#define endl '\n'

void dbg() { std::cout << "  #\n"; }

template<typename T, typename...Args>
void dbg(T a, Args...args) {
    std::cout << a << ' ';
    dbg(args...);
}

void io() { ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); }

#define typeinput int

inline char nc() {
    static char buf[1000000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin), p1 == p2) ? EOF : *p1++;
}

inline void read(typeinput &sum) {
    char ch = nc();
    sum = 0;
    while (!(ch >= '0' && ch <= '9')) ch = nc();
    while (ch >= '0' && ch <= '9') sum = (sum << 3) + (sum << 1) + (ch - 48), ch = nc();
}
const int maxn = 2e5+5;
int a[maxn],q[maxn];
int rig[maxn]; // 右边第一个比他大的位置
ll dp[maxn]; // 以当前作为l的值的和
stack<pair<int,int>> st; // 单调栈
int sa[maxn],rk[maxn],oldrk[maxn<<1],id[maxn],px[maxn],cnt[maxn];
int ht[maxn];

bool cmp(int x,int y,int w) {
    return oldrk[x] == oldrk[y] && oldrk[x+w] == oldrk[y+w];
}
// 这东西值域有点大,离散化
void getheight(int n,int m) {
    int i,w,p,k;
    for(i=1;i<=m;++i) cnt[i]=0;
    for(i=1;i<=n;++i) ++cnt[rk[i]=a[i]];
    for(i=1;i<=m;++i) cnt[i] += cnt[i-1];
    for(i=n;i;--i) sa[cnt[rk[i]]--] = i;
    for(w=1;;w<<=1,m=p) {
        for(p=0,i=n;i>n-w;--i) id[++p] = i;
        for(i=1;i<=n;++i) if(sa[i]>w) id[++p] = sa[i]-w;
        memset(cnt,0,sizeof(cnt));
        for(i=1;i<=n;++i) ++cnt[px[i]=rk[id[i]]];
        for(i=1;i<=m;++i) cnt[i] += cnt[i-1];
        for(i=n;i>=1;--i) sa[cnt[px[i]]--] = id[i];
        memcpy(oldrk,rk,sizeof(rk));
        for(p=0,i=1;i<=n;++i) rk[sa[i]] = cmp(sa[i],sa[i-1],w)?p:++p;
        if(p==n) {
            for(int i=1;i<=n;++i) sa[rk[i]] = i;
            break;
        }
    }
    for(i=1,k=0;i<=n;++i) {
        if(k) --k;
        while(sa[rk[i]-1]+k<=n && a[i+k]==a[sa[rk[i]-1]+k]) ++k;
        ht[rk[i]] = k;
    }
}
int ql,qr;
int pos[maxn<<2];
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
int query(int l,int r,int rt) {
    if(ql<=l&&qr>=r) {
        return pos[rt];
    }
    int mid=l+r>>1;
    int ret=0;
    if(ql<=mid) {
        int tmp=query(lson);
        if(a[tmp]>a[ret]) ret=tmp;
    }
    if(qr>mid) {
        int tmp=query(rson);
        if(a[tmp]>a[ret]) ret=tmp;
    }
    return ret;
}
void build(int l,int r,int rt) {
    if(l==r) {
        pos[rt] = l;
        return;
    }
    int mid=l+r>>1;
    build(lson);
    build(rson);
    if(a[pos[rt<<1]]>a[pos[rt<<1|1]]) pos[rt]=pos[rt<<1];
    else pos[rt]=pos[rt<<1|1];
}
signed main() {
    io();
    int t;cin>>t;
    while(t--) {
        int n;cin>>n;
        for(int i=1;i<=n;++i) {
            cin>>a[i];
            q[i] = a[i];
        }
        // 离散化
        sort(q+1,q+1+n);
        int tot = unique(q+1,q+1+n)-q-1;
        for(int i=1;i<=n;++i) a[i] = lower_bound(q+1,q+1+tot,a[i])-q;
        // 单调栈
        dp[n+1]=0;
        while(st.size()) st.pop();
        st.push(mkp(1e7,n+1));
        for(int i=n;i;--i) {
            while(st.size() && st.top().fi < a[i]) st.pop();
            rig[i] = st.top().se;
            dp[i] = dp[rig[i]] + 1ll*q[a[i]]*(rig[i]-i);
            st.push(mkp(a[i],i));
        }
        // height数组
        getheight(n,tot);
        // 查询区间
        ll ans = 0;
        build(1,n,1);
        for(int i=1;i<=n;++i) {
            // i-ht[i]中最大值的位置
            // 以及该位置的rig
            if(ht[rk[i]]==0) {
                ans += dp[i];
                continue;
            }
            ql = i,qr = i+ht[rk[i]]-1;
            int j = query(1,n,1);
            ans += dp[rig[j]]+1ll*q[a[j]]*(rig[j]-i-ht[rk[i]]);
        }
        cout<<ans<<endl;
    }
}

Journey among Railway Stations(线段树)

标程:带着结构体跑。

#include<bits/stdc++.h>
#define N 1000005
using namespace std;
typedef long long LL;
const LL oo = 1e12;
struct Function{
    LL l, r, t; int valid;
    Function():l(0), r(0), t(0), valid(0){}
    Function(LL l_, LL r_, LL t_):l(l_), r(r_), t(t_), valid(1){}
}a[N << 2], ans;
int st[N], ed[N], dist[N];
Function Merge(const Function &A, const Function &B, int d){
    if (!A.valid || !B.valid || A.t + d > B.r) return Function();
    //if (A.t + d + A.r - A.l <= B.l) return Function(A.r, A.r, B.t);
    LL newr = A.t + d + A.r - A.l > B.r ? B.r + A.l - A.t - d : A.r, newl, newt;
    if (A.t + d >= B.l) newl = A.l, newt = B.t + A.t + d - B.l;
    else newl = min(newr, A.l + B.l - A.t - d), newt = B.t;
	return Function(newl, newr, newt);
}
void build(int x, int l, int r){
    if (l == r){
        a[x] = Function(st[l], ed[l], st[l]);
        //printf("%d %d %lld %lld %lld %d\n", l, r, a[x].l, a[x].r, a[x].t, a[x].valid);
        return;
    }
    int mid = (l + r) >> 1;
    build(x << 1, l, mid);
    build(x << 1 | 1, mid + 1, r);
    a[x] = Merge(a[x << 1], a[x << 1 | 1], dist[mid]);
    //printf("%d %d %lld %lld %lld %d\n", l, r, a[x].l, a[x].r, a[x].t, a[x].valid);
}
void UpdateDist(int x, int l, int r, int pos){
    if (l == r) return;
    int mid = (l + r) >> 1;
    if (pos < mid) UpdateDist(x << 1, l, mid, pos);
    if (pos > mid) UpdateDist(x << 1 | 1, mid + 1, r, pos);
    a[x] = Merge(a[x << 1], a[x << 1 | 1], dist[mid]);
}
void UpdateNode(int x, int l, int r, int pos){
    if (l == r){
        a[x] = Function(st[l], ed[l], st[l]);
        return;
    }
    int mid = (l + r) >> 1;
    if (pos <= mid) UpdateNode(x << 1, l, mid, pos); 
               else UpdateNode(x << 1 | 1, mid + 1, r, pos);
    a[x] = Merge(a[x << 1], a[x << 1 | 1], dist[mid]);
}
void Query(int x, int l, int r, int ql, int qr){
    if (ql <= l && r <= qr){
        if (ql == l) ans = a[x]; else ans = Merge(ans, a[x], dist[l-1]);
        return;// TODO: test
    }
    int mid = (l + r) >> 1;
    if (ql <= mid) Query(x << 1, l, mid, ql, qr);
    if (qr > mid) Query(x << 1 | 1, mid + 1, r, ql, qr);
}
int main(){
	//freopen("sample.txt", "r", stdin);
    int T; scanf("%d", &T);
    while (T--){
        int n; scanf("%d", &n);
        for (int i = 1; i <= n; i++)
			scanf("%d", &st[i]);
		for (int i = 1; i <= n; i++)
			scanf("%d", &ed[i]);
        for (int i = 1; i < n; i++)
            scanf("%d", &dist[i]);
        build(1, 1, n);
        int Q; scanf("%d", &Q);
        while (Q--){
            int type; scanf("%d", &type);
            if (!type){
                int l, r; scanf("%d%d", &l, &r);
//                ans = Function(-oo, oo, -oo);
                Query(1, 1, n, l, r);
                puts(ans.valid ? "Yes" : "No");
            }
            else if (type == 1){
                int pos, new_dist;
                scanf("%d%d", &pos, &new_dist);
                assert(pos >= 1 && pos < n);
                dist[pos] = new_dist;
                UpdateDist(1, 1, n, pos);
            }
            else{
                int pos; scanf("%d", &pos);
                scanf("%d%d", &st[pos], &ed[pos]);
                UpdateNode(1, 1, n, pos);
            }
        }
    }
}

法2:很自然的思路,但是我没想到怎么处理合并以及区间末尾变化这一点。
区间末尾变化:将v也附带上cost的后缀。
合并:只需要判断是否left u’<right v’

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6+5;
const int oo = 2e9;
struct node{
    ll mxu,mnv;
    int ok;
    node():mxu(0),mnv(oo),ok(1){}
    node(ll mx,ll mn,int o):mxu(mx),mnv(mn),ok(o){}
}a[maxn<<2],ans;
ll lzy[maxn<<2];

int u[maxn],v[maxn],cost[maxn];
ll suf[maxn]; // 后缀cost
int ql,qr,w,p,q;
node merge(node &ls,node &rs) {
    int okk;
    ll mx,mn;
    if(ls.ok&&rs.ok&&ls.mxu<=rs.mnv) okk=1;
    else okk=0;
    mx=max(ls.mxu,rs.mxu);
    mn=min(ls.mnv,rs.mnv);
    return node(mx,mn,okk);
}

// 对cost进行修改,那么在他前面,(包括他这个位置)的u以及v都进行改变
void push_down(int l,int r,int rt) {
    if(lzy[rt]) {
        a[rt<<1].mnv+=lzy[rt]; // w是改变值
        a[rt<<1].mxu+=lzy[rt];
        lzy[rt<<1]+=lzy[rt];
        a[rt<<1|1].mnv+=lzy[rt]; // w是改变值
        a[rt<<1|1].mxu+=lzy[rt];
        lzy[rt<<1|1]+=lzy[rt];
        lzy[rt]=0;
    }
}

void build(int l,int r,int rt) {
    if(l==r) {
        a[rt] = node(suf[l]+u[l],suf[l]+v[l],1);
        lzy[rt] = 0;
        return;
    }
    int mid=l+r>>1;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
    a[rt] = merge(a[rt<<1],a[rt<<1|1]);
    lzy[rt] = 0;
}

void chcost(int l,int r,int rt) {
//    cout<<l<<" "<<r<<endl;
    if(ql<=l&&qr>=r) {
        a[rt].mnv += w;
        a[rt].mxu += w;
        lzy[rt] += w;
        return;
    }
    push_down(l,r,rt);
    int mid=l+r>>1;
    if(ql<=mid) chcost(l,mid,rt<<1);
    if(qr>mid) chcost(mid+1,r,rt<<1|1);
    a[rt] = merge(a[rt<<1],a[rt<<1|1]);
}

void chuv(int l,int r,int rt) {
    if(l==r) {
        a[rt].mxu+=p; // 改变量
        a[rt].mnv+=q;
        return;
    }
    push_down(l,r,rt);
    int mid=l+r>>1;
    if(ql<=mid) chuv(l,mid,rt<<1);
    else chuv(mid+1,r,rt<<1|1);
    a[rt]=merge(a[rt<<1],a[rt<<1|1]);
}

void query(int l,int r,int rt) {
    if(ql<=l&&qr>=r) {
        if(ql==l) ans=a[rt];
        else ans=merge(ans,a[rt]);
        return;
    }
    int mid=l+r>>1;
    push_down(l,r,rt);
    if(ql<=mid) query(l,mid,rt<<1);
    if(qr>mid) query(mid+1,r,rt<<1|1);
}

int main() {
    int t;scanf("%d",&t);
    while(t--) {
        int n;scanf("%d",&n);
        for(int i=1;i<=n;++i) scanf("%d",&u[i]);
        for(int i=1;i<=n;++i) scanf("%d",&v[i]);
        for(int i=1;i<n;++i) scanf("%d",&cost[i]);
        int qq;scanf("%d",&qq);
        suf[n]=0;
        for(int i=n-1;i;--i) suf[i]=suf[i+1]+cost[i];
        build(1,n,1);
        while(qq--) {
            int opt;scanf("%d",&opt);
            if(!opt) {
                scanf("%d%d",&ql,&qr);
                query(1,n,1);
                if(ans.ok) printf("Yes\n");
                else printf("No\n");
            } else if(opt==1) {
                int nw;
                ql=1;
                scanf("%d%d",&qr,&nw);
                w = nw-cost[qr];
                cost[qr] = nw;
                chcost(1,n,1);
            } else {
                int np,nq;
                scanf("%d%d%d",&ql,&np,&nq);
                p = np-u[ql],q = nq-v[ql];
                u[ql]=np,v[ql]=nq;
                chuv(1,n,1);
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值