八月刷题记录3

19 篇文章 0 订阅
11 篇文章 0 订阅

牛客多校10 A Browser Games

思路很简单,在trie上建虚树,然后回溯染色处理。

//
// Created by Artist on 2021/8/26.
//

#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)

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

template<typename T, typename...Args>
void dbg(T a, Args...args) {
    std::cout << a << ' ';
    dbg(args...);
}
const int maxn = 1e5+5;
char s[maxn][105];
int lst[maxn],stk[maxn<<1],tot,len[maxn],d[maxn<<1],p[maxn],fa[maxn<<1],ret[maxn<<1],sz[maxn<<1];

bool cmp(int x,int y){
    for(int i=1;i<=min(len[x],len[y]);++i){
        if(s[x][i]<s[y][i]) return 1;
        if(s[x][i]>s[y][i]) return 0;
    }
}

void addedge(int x,int y){
    fa[y]=x;
}

int lca(int x,int y){
    for(int i=1;i<=min(len[x],len[y]);++i){
        if(s[x][i]!=s[y][i]) return i-1;
    }
}

signed main(){
    int n;scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%s",s[i]+1);
        len[i]=strlen(s[i]+1);
        p[i]=i;
    }
    // 先排序(建成trie)
    sort(p+1,p+1+n,cmp);

    // 再建虚树
    tot=stk[*stk=1]=1;
    for(int i=1;i<=n;++i){
        int x=++tot; // 该字符串末尾对应的点(关键点)
        d[x]=len[p[i]];
        if(i==1) stk[++*stk]=x;
        else {
            int t=lca(p[i],p[i-1]); // lca的dfn
            while(*stk>1 && t<=d[stk[*stk-1]]) addedge(stk[*stk-1],stk[*stk]),--*stk;
            if(t!=d[stk[*stk]]) addedge(++tot,stk[*stk]),d[tot]=t,stk[*stk]=tot;
            stk[++*stk]=x;
        }
        lst[p[i]]=x;
    }
    while(*stk>1) addedge(stk[*stk-1],stk[*stk]),--*stk;

    // 处理答案
    sz[1]=1; // 因为答案不能为0,所以令根节点与其他节点不一致
    int ans=0;
    for(int i=1;i<=n;++i){
        for(int j=lst[i];j;j=fa[j]){
            ++sz[j];
        }
    }
    for(int i=1;i<=n;++i){
        for(int j=lst[i],k=0;j;k=j,j=fa[j]){
            if(k && !sz[k]) ++ret[j],++ans;
            if(!(--sz[j])) ans -= ret[j];
        }
        printf("%d\n",ans);
    }
}

牛客多校7 J - xay loves floyd

for i from 1 to n
  for j from 1 to n
    for k from 1 to n
      dis[i][j] <- min(dis[i][j], dis[i][k] + dis[k][j])

方法1

用dijkstra模拟这种floyd的跑法。

注意到,dis[i][j]正确当且仅当以下两个条件之一满足:(1)dis[i][j]本身正确(即,存在一条连接这两点的边,就是这两点的最短路)(2)dis[i][k]dis[k][j]均正确,且k位于i到j的一个最短路上

我们先对每一个点跑一遍dijkstra得到正确的最短路数据。然后将图删剩仅满足条件(1)的边。

接着,再对每一个点跑一遍dijkstra,此时是在模拟错误的floyd,得到的结果是错误的floyd的结果。

我们记 g [ s ] [ t ] g[s][t] g[s][t] 表示 s s s t t t 应用错误的 floyd 跑出的答案。

枚举中转点k,我们将(i,k)向(i,j)更新。注意必须有 j ≥ k j\geq k jk 更新才成立。

我们不以距离作为优先队列中用来排序的值。我们以中转点的序号作为用来排序的值,从小到大更新。

如果更新出的dis正确,将该路径当作一条边放入图中。

//
// Created by Artist on 2021/8/27.
//

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
#define mkp make_pair
#define pii pair<int,int>
#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)

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

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

const int maxn = 2e3+4,maxm = 5e3+4;
const int inf = 0x3f3f3f3f;
vector<pii > G[maxn];
int g[maxn][maxn],dis[maxn][maxn];
priority_queue<pii,vector<pii >,greater<pii > > pq;
int n;

void dijkstra(int st){
    for(int i=1;i<=n;++i) dis[st][i]=inf;
    dis[st][st]=0;
    while(!pq.empty()) pq.pop();
    pq.push(mkp(0,st));
    while(!pq.empty()){
        pii cur=pq.top();pq.pop();
        int u=cur.se,w=cur.fi;
        if(dis[st][u]<w) continue;
        for(auto [ww,v]:G[u]){
            if(dis[st][v]>w+ww){
                dis[st][v]=w+ww;
                pq.push({dis[st][v],v});
            }
        }
    }
    G[st].clear();
    // 清空并不会影响接下来的正确答案的dijkstra
    for(int i=1;i<=n;++i){
        if(dis[st][i]==g[st][i] && dis[st][i]<inf && st!=i)
            G[st].pb(mkp(dis[st][i],i));
    }
}

void solve(int st){
    while(!pq.empty()) pq.pop();
    for(auto i:G[st]) pq.push({0,i.se});
    // first为中转点k
    while(!pq.empty()){
        pii cur=pq.top();pq.pop();
        int u=cur.se,id=cur.fi;
        for(auto [ww,v]:G[u]){
            if(v<id) continue; // 只能往后更新
            if(g[st][v]==dis[st][v]||dis[st][v]>=inf) continue;
            if(dis[st][v]==dis[st][u]+ww){
                pq.push({v,v});
                G[st].push_back({dis[st][v],v});
                g[st][v]=dis[st][v];
            }
        }
    }
}

signed main() {
    int m;scanf("%d%d",&n,&m);
    memset(g,0x3f,sizeof(g));
    for(int i=1,u,v,w;i<=m;++i){
        scanf("%d%d%d",&u,&v,&w);
        G[u].pb(mkp(w,v));
        g[u][v]=w;
    }
    for(int i=1;i<=n;++i) g[i][i]=0;
    for(int i=1;i<=n;++i){
        dijkstra(i);
    }
    for(int i=1;i<=n;++i){
        solve(i);
    }
    int ans=0;
    for(int i=1;i<=n;++i) for(int j=1;j<=n;++j)
        if(g[i][j]==dis[i][j]||(g[i][j]>=inf&&dis[i][j]>=inf))
            ans++;
    printf("%d\n",ans);
}

方法2

这个依旧成立:dis[i][j]正确当且仅当以下两个条件之一满足:(1)dis[i][j]本身正确(即,存在一条连接这两点的边,就是这两点的最短路)(2)dis[i][k]dis[k][j]均正确,且k位于i到j的一个最短路上

那么考虑设 g [ i ] [ j ] g[i][j] g[i][j] i i i j j j 的所有最短路上的点构成的集合。

f 1 [ i ] [ j ] f1[i][j] f1[i][j] 表示 d i s [ i ] [ j ] dis[i][j] dis[i][j] 是正确的, f 2 [ i ] [ j ] f2[i][j] f2[i][j] 表示 d i s [ j ] [ i ] dis[j][i] dis[j][i] 是正确的。

暴力用上述条件判断,是 O ( n 3 ) O(n^3) O(n3)的:

我们枚举每一个 i i i,再枚举 j j j,再枚举 k k k ,让 f 1 [ i ] [ k ] , f 2 [ k ] [ j ] f1[i][k],f2[k][j] f1[i][k],f2[k][j] 都为真,且 k ∈ g [ i ] [ j ] k\in g[i][j] kg[i][j]

那么我们考虑用bitset进行位压,优化转移。

我们把枚举 k k k 这一维度给优化掉。

因此变成:

for(int i=1;i<=n;++i){
    for(int j=1;j<=n;++j){
        if((f1[i]&f2[j]&g[j]).any()){
            f1[i].set(j);
            f2[j].set(i);
        }
    }
}
//
// Created by Artist on 2021/8/27.
//

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
#define mkp make_pair
#define pii pair<int,int>
#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)

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

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

const int maxn = 2e3+4,maxm = 5e3+4;
const int inf = 0x3f3f3f3f;
vector<pii > G[maxn];
int dis[maxn][maxn];
int tmp[maxn]; // 暂存序号
bitset<maxn> f1[maxn],f2[maxn],g[maxn];
int ID;
priority_queue<pii,vector<pii >,greater<pii > > pq;
int n;

void dijkstra(int s,int* d){
    d[s]=0;
    pq.push({0,s});
    while(!pq.empty()){
        pii cur=pq.top();pq.pop();
        int u=cur.se,w=cur.fi;
        if(d[u]<w) continue;
        for(auto [v,ww]:G[u]){
            if(dis[s][v]>w+ww){
                dis[s][v]=w+ww;
                pq.push({dis[s][v],v});
            }
        }
    }
}

bool cmp(int x,int y){
    return dis[ID][x] < dis[ID][y];
}

signed main() {
    int m;scanf("%d%d",&n,&m);
    memset(dis,0x3f,sizeof(dis));
    for(int i=1,u,v,w;i<=m;++i){
        scanf("%d%d%d",&u,&v,&w);
        G[u].pb({v,w});
    }

    // 计算正确答案
    memset(dis,0x3f,sizeof(dis));
    for(int i=1;i<=n;++i){
        dijkstra(i,dis[i]);
        f1[i].set(i);
        f2[i].set(i);
    }

    // 第一种情况
    for(int u=1;u<=n;++u){
        for(auto [v,w]:G[u]){
            if(dis[u][v]==w){
                f1[u].set(v);
                f2[v].set(u);
            }
        }
    }

    for(int i=1;i<=n;++i){
        // 先更新g
        for(int j=1;j<=n;++j){
            tmp[j]=j;
            g[j].reset();
            g[j].set(j);
        }
        ID=i;
        sort(tmp+1,tmp+1+n,cmp);
        for(int j=1;j<=n;++j){
            int u=tmp[j];
            for(auto [v,w]:G[u]){
                if(dis[i][v]==dis[i][u]+w){
                    g[v] |= g[u];
                }
            }
        }

        // 再更新错误答案
        for(int j=1;j<=n;++j){
            if((f1[i]&f2[j]&g[j]).any()){
                f1[i].set(j);
                f2[j].set(i);
            }
        }
    }
    int ans=0;

    for(int i=1;i<=n;++i) ans += f1[i].count();
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            ans += dis[i][j]==inf;
    printf("%d\n",ans);
}

P1050 [NOIP2005 普及组] 循环

假的扩展欧拉定理。。思路是题解1。

//
// Created by Artist on 2021/8/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)

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

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

const int N = 1e3 + 3; // 数组大小(大数长度)
const int base = 1e8; // 压位,数组中一个位置储存一个1e8的数
int aux[N << 3];

struct bigint {
    int s[N], l; // s[1]是最低位(是一个小于1e8的数)
    void CL() {
        l = 0;
        memset(s, 0, sizeof(s));
    }

    void pr() {
        printf("%d", s[l]);
        for (int i = l - 1; i; i--)
            printf("%08d", s[i]);
    }

    void re_l() {
        int i, x = 0, k = 1, L = 0, fl, o;
        char c = getchar();
        for (; c < '0' || c > '9'; c = getchar());
        for (; c >= '0' && c <= '9'; c = getchar()) {
            if (!(L - 1) && !aux[L])
                L--;
            aux[++L] = c - '0';
        }
        CL();
        l = L / 8 + ((o = L % 8) > 0);
        for (i = 1; i <= o; i++)
            x = x * 10 + aux[i];
        if (o)
            s[l] = x;
        fl = !o ? l + 1 : l;
        for (i = o + 1, x = 0; i <= L; i++, k++) {
            x = x * 10 + aux[i];
            if (!(k ^ 8)) {
                s[--fl] = x;
                x = k = 0;
            }
        }
        if (!l)
            l = 1;
    }

    ll toint()  // transfer to integer
    {
        ll x = 0;
        for (int i = l; i; i--)
            x = x * base + s[i];
        return x;
    }

    bigint operator=(int b) {
        CL();
        do {
            s[++l] = b % base;
            b /= base;
        } while (b > 0);
        return *this;
    }

    bigint operator=(ll b) {
        CL();
        do {
            s[++l] = b % base;
            b /= base;
        } while (b > 0);
        return *this;
    }

    bigint operator+(const int &b) {
        bigint c = *this;
        ll x = b;
        for (int i = 1; i <= l && x; i++) {
            x = x + c.s[i];
            c.s[i] = x % base;
            x /= base;
        }
        if (x)
            c.s[++c.l] = x;
        return c;
    }

    bigint operator+(const ll &b) {
        bigint c = *this;
        ll x = b;
        for (int i = 1; i <= l && x; i++) {
            x = x + c.s[i];
            c.s[i] = x % base;
            x /= base;
        }
        if (x)
            c.s[++c.l] = x;
        return c;
    }

    bigint operator+(bigint &b) {
        if (b.l < 3)
            return *this + b.toint();
        bigint c;
        ll x = 0;
        int k = l < b.l ? b.l : l;
        c.CL();
        c.l = k;
        for (int i = 1; i <= k; i++) {
            x = x + s[i] + b.s[i];
            c.s[i] = x % base;
            x /= base;
        }
        if (x)
            c.s[++c.l] = x;
        return c;
    }

    bigint operator-(const bigint &b) {
        bigint c, d = *this;
        ll x = 0;
        c.CL();
        for (int i = 1; i <= l; i++) {
            if ((x = d.s[i]) < b.s[i]) {
                d.s[i + 1]--;
                x += base;
            }
            c.s[i] = x - b.s[i];
        }
        c.l = l;
        for (; !c.s[c.l] && c.l > 1; c.l--);
        return c;
    }

    bigint operator-(const int &b) {
        bigint c;
        return *this - (c = b);
    }

    bigint operator-(const ll &b) {
        bigint c;
        return *this - (c = b);
    }

    bigint operator*(const int &b) {
        bigint c;
        ll x = 0;
        c.CL();
        for (int i = 1; i <= l; i++) {
            x = x + 1LL * s[i] * b;
            c.s[i] = x % base;
            x /= base;
        }
        for (c.l = l; x; x /= base)
            c.s[++c.l] = x % base;
        return c;
    }

    bigint operator*(bigint &b) {
        if (b.l < 2)
            return *this * b.toint();
        bigint c;
        ll x;
        int i, j, k;
        c.CL();
        for (i = 1; i <= l; i++) {
            x = 0;
            for (j = 1; j <= b.l; j++) {
                x = x + 1LL * s[i] * b.s[j] + c.s[k = i + j - 1];
                c.s[k] = x % base;
                x /= base;
            }
            if (x)
                c.s[i + b.l] = x;
        }
        for (c.l = l + b.l; !c.s[c.l] && c.l > 1; c.l--);
        return c;
    }

    bigint operator*(const ll &b) {
        bigint c;
        if (b > 2e9) {
            c = b;
            return *this * c;
        }
        ll x = 0;
        c.CL();
        for (int i = 1; i <= l; i++) {
            x = x + b * s[i];
            c.s[i] = x % base;
            x /= base;
        }
        for (c.l = l; x; x /= base)
            c.s[++c.l] = x % base;
        return c;
    }

    bigint operator/(const int &b) {
        bigint c;
        ll x = 0;
        c.CL();
        for (int i = l; i; i--) {
            c.s[i] = (x * base + s[i]) / b;
            x = (x * base + s[i]) % b;
        }
        for (c.l = l; !c.s[c.l] && c.l > 1; c.l--);
        return c;
    }

    bigint operator/(const ll &b) {
        bigint c;
        ll x = 0;
        c.CL();
        for (int i = l; i; i--) {
            c.s[i] = (x * base + s[i]) / b;
            x = (x * base + s[i]) % b;
        }
        for (c.l = l; !c.s[c.l] && c.l > 1; c.l--);
        return c;
    }

    bigint operator/(bigint &b) {
        if (b.l < 2)
            return *this / b.toint();
        bigint c, d;
        int i, j, le, r, mid, k;
        c.CL();
        d.CL();
        for (i = l; i; i--) {
            for (j = ++d.l; j > 1; j--)
                d.s[j] = d.s[j - 1];
            d.s[1] = s[i];
            if (d < b)
                continue;
            le = k = 0;
            r = base - 1;
            while (le <= r) {
                mid = (le + r) >> 1;
                if (b * mid <= d) {
                    le = mid + 1;
                    k = mid;
                } else
                    r = mid - 1;
            }
            c.s[i] = k;
            d = d - b * k;
        }
        for (c.l = l; !c.s[c.l] && c.l > 1; c.l--);
        return c;
    }

    bigint operator%(const int &b) {
        bigint c;
        ll x = 0;
        c.CL();
        for (int i = l; i; i--)
            x = (x * base + s[i]) % b;
        return c = x;
    }

    bigint operator%(const ll &b) {
        bigint c;
        ll x = 0;
        c.CL();
        for (int i = l; i; i--)
            x = (x * base + s[i]) % b;
        return c = x;
    }

    bigint operator%(bigint &b) {
        if (b.l < 2)
            return *this % b.toint();
        bigint c;
        int i, j, le, r, mid, k;
        c.CL();
        for (i = l; i; i--) {
            for (j = ++c.l; j > 1; j--)
                c.s[j] = c.s[j - 1];
            c.s[1] = s[i];
            if (c < b)
                continue;
            le = k = 0;
            r = base - 1;
            while (le <= r) {
                mid = (le + r) >> 1;
                if (b * mid <= c) {
                    le = mid + 1;
                    k = mid;
                } else
                    r = mid - 1;
            }
            c = c - b * k;
        }
        for (; !c.s[c.l] && c.l > 1; c.l--);
        return c;
    }

    bigint operator+=(bigint &b) {
        return *this = *this + b;
    }

    bigint operator+=(ll &b) {
        return *this = *this + b;
    }

    bigint operator+=(int &b) {
        return *this = *this + b;
    }

    bigint operator-=(bigint &b) {
        return *this = *this - b;
    }

    bigint operator-=(ll &b) {
        return *this = *this - b;
    }

    bigint operator-=(int &b) {
        return *this = *this - b;
    }

    bigint operator*=(bigint &b) {
        return *this = *this * b;
    }
    bigint operator*=(ll &b) {
        return *this = *this * b;
    }
    bigint operator*=(int &b) {
        return *this = *this * b;
    }
    bigint operator/=(bigint &b) {
        return *this = *this / b;
    }
    bigint operator/=(ll &b) {
        return *this = *this / b;
    }
    bigint operator/=(int &b) {
        return *this = *this / b;
    }
    bigint operator%=(bigint &b) {
        return *this = *this % b;
    }
    bigint operator%=(ll &b) {
        return *this = *this % b;
    }
    bigint operator%=(int &b) {
        return *this = *this % b;
    }
    bool operator<(const bigint &b) const {
        if (l ^ b.l)
            return l < b.l;
        for (int i = l; i; i--)
            if (s[i] ^ b.s[i])
                return s[i] < b.s[i];
        return false;
    }
    bool operator<=(const bigint &b) const {
        if (l ^ b.l)
            return l < b.l;
        for (int i = l; i; i--)
            if (s[i] ^ b.s[i])
                return s[i] < b.s[i];
        return true;
    }
    bool operator>(const bigint &b) const {
        if (l ^ b.l)
            return l > b.l;
        for (int i = l; i; i--)
            if (s[i] ^ b.s[i])
                return s[i] > b.s[i];
        return false;
    }
    bool operator>=(const bigint &b) const {
        if (l ^ b.l)
            return l > b.l;
        for (int i = l; i; i--)
            if (s[i] ^ b.s[i])
                return s[i] > b.s[i];
        return true;
    }
    bool operator==(const bigint &b) const {
        if (l ^ b.l)
            return false;
        for (int i = l; i; i--)
            if (s[i] ^ b.s[i])
                return false;
        return true;
    }
    bool operator!=(const bigint &b) const {
        if (l ^ b.l)
            return true;
        for (int i = l; i; i--)
            if (s[i] ^ b.s[i])
                return true;
        return false;
    }
    bool operator<(ll b) const {
        bigint c;
        return *this < (c = b);
    }
    bool operator<=(ll b) const {
        bigint c;
        return *this <= (c = b);
    }
    bool operator>(ll b) const {
        bigint c;
        return *this > (c = b);
    }
    bool operator>=(ll b) const {
        bigint c;
        return *this >= (c = b);
    }
    bool operator==(ll b) const {
        bigint c;
        return *this == (c = b);
    }
    bool operator!=(ll b) const {
        bigint c;
        return *this != (c = b);
    }
    bool operator<(int b) const {
        bigint c;
        return *this < (c = b);
    }
    bool operator<=(int b) const {
        bigint c;
        return *this <= (c = b);
    }
    bool operator>(int b) const {
        bigint c;
        return *this > (c = b);
    }
    bool operator>=(int b) const {
        bigint c;
        return *this >= (c = b);
    }
    bool operator==(int b) const {
        bigint c;
        return *this == (c = b);
    }
    bool operator!=(int b) const {
        bigint c;
        return *this != (c = b);
    }
};

bigint ksm(bigint x, bigint y, bigint md) {
    bigint s;
    s = 1;
    for (; y > 0; y = y / 2, x = x * x % md)
        if (y.s[1] & 1)
            s = s * x % md;
    return s;
}

bigint n,modd,ans,tmp,tmp2,tmp3;

signed main() {
    n.re_l();
    ans=1;
    modd=1;
    int c10=10;
    int k; scanf("%d", &k);
    for(int i=1;i<=k;++i){
        modd*=c10; // mod
        tmp=n%modd;
        tmp2 = ksm(tmp,ans,modd);
        tmp3 = tmp2*tmp%modd;
       // tmp2.pr();putchar(10);
       // tmp3.pr();putchar(10);
        int xhj = 1;
        while(tmp3!=tmp){
            tmp3 = tmp3*tmp2%modd;
           // tmp3.pr();
           // putchar(10);
            xhj++;
            // 不存在(比如10 10)
            if(xhj>30) {
                printf("-1\n");
                return 0;
            }
        }
       // DB1(xhj);
        ans *= xhj;
    }
    ans.pr();
}

洛谷 打砖块

首先观察到,如果我们当时正在打的子弹是最后一颗子弹,那么打掉一个N后,就算下一个砖块是Y,我们也没有子弹去打他。因此,我们把情况分为:当前子弹是最后一颗子弹,当前子弹不是最后一颗子弹。

那么,考虑 [ 1 , i − 1 ] [1,i-1] [1,i1] 列和第 i i i 列,情况就可以分为:

(1)第 i i i 列一个也不打,dp直接继承。

(2)最后一颗子弹在第 i i i 列被打掉。

(3)最后一颗子弹在第 [ 1 , i − 1 ] [1,i-1] [1,i1] 列被打掉。

(4)最后一颗子弹不在 [ 1 , i ] [1,i] [1,i] 列被打掉。(那么这一颗必然在 [ i + 1 , n ] [i+1,n] [i+1,n]中。但我们只考虑到第i列所以不管)

这些情况是完备的。

于是我们可以定义状态:

d p [ i ] [ j ] [ 0 ] dp[i][j][0] dp[i][j][0]:到第i列,花了j个子弹,最后一颗子弹在 [ 1 , i ] [1,i] [1,i]中,最大的收益。

d p [ i ] [ j ] [ 1 ] dp[i][j][1] dp[i][j][1]:到第i列,花了j个子弹,最后一颗子弹不在 [ 1 , i ] [1,i] [1,i]中,最大的收益。

定义辅助数组:

s u m [ i ] [ j ] [ 0 ] sum[i][j][0] sum[i][j][0]:打第i列,打到第j个砖块,刚好打完子弹,后面有Y也打不成(即最后一颗子弹打在这一列)的收益。

s u m [ i ] [ j ] [ 1 ] sum[i][j][1] sum[i][j][1]:打第i列,打到第j个砖块,后面有Y就继续推(打这一列的时候子弹充足),的收益。

t o t [ i ] [ j ] tot[i][j] tot[i][j]:打第i列,达到第j个砖块,花费的子弹数目。

那么转移就可以写成:

// 第i列一个也不打,直接继承
dp[i][j][0]=dp[i-1][j][0];
dp[i][j][1]=dp[i-1][j][1];

// 最后一颗子弹在[1,i-1]中
dp[i][j][0]=dp[i-1][j-tot[i][k]][0]+sum[i][k][1];
// 最后一颗子弹在i中
dp[i][j][0]=dp[i-1][j-tot[i][k]][1]+sum[i][k][0];
// 最后一颗子弹不在[1,i]中
dp[i][j][1]=dp[i-1][j-tot[i][k]][1]+sum[i][k][1];
//
// Created by Artist on 2021/8/31.
//

#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()
template<class T>void ChkMax(T &a,T b){a=a<b?b:a;}
const int INF=0x3f3f3f3f;
#define DB1(args...) do { cout << #args << " : "; dbg(args); } while (0)

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

template<typename T, typename...Args>
void dbg(T a, Args...args) {
    std::cout << a << ' ';
    dbg(args...);
}
const int inf = 0x3f3f3f3f;
const int maxn = 203;
ll f[maxn][maxn],c[maxn][maxn];

ll sum[maxn][maxn][2];//0:最后一颗子弹在i,1:最后一颗子弹不在i
ll dp[maxn][maxn][2];
int cur[maxn],tot[maxn][maxn];

signed main() {
    // 行数,列数
    int n,m,K;scanf("%d%d%d",&n,&m,&K);
    for(int i=n;i;--i){
        for(int j=1;j<=m;++j){
            scanf("%lld",&f[j][i]);
            char ch[4];scanf("%s",ch);
            if(ch[0]=='Y') c[j][i]=1;
            else c[j][i]=0;
        }
    }
    ll ans=0;
    // 用一颗子弹把前面的Y全部打掉
    for(int i=1;i<=m;++i){
        for(int j=1;j<=n;++j){
            cur[i]=j;
            if(!c[i][j]) break;
            ans += f[i][j];
        }
    }
    // 预处理sum数组、tot数组
    for(int i=1;i<=m;++i){
        // nxt:把Y全部压掉,跑的都是N
        for(int j=cur[i],nxt;j<=n;j=nxt){
            tot[i][j]++;
            nxt=j+1;
            sum[i][j][0]+=f[i][j];
            sum[i][j][1]+=f[i][j];
            ll sm=0;
            while(nxt<=n && c[i][nxt]) sm+=f[i][nxt],nxt++;
            sum[i][j][1]+=sm;
            if(nxt>n) break;
            sum[i][nxt][0]+=sum[i][j][0];
            sum[i][nxt][1]+=sum[i][j][1];
            tot[i][nxt]+=tot[i][j];
        }
    }
    for(int i=0;i<=m;++i) dp[i][0][0]=-inf;
    // 第几列
    for(int i=1;i<=m;++i){
        // 背包容量
        for(int j=1;j<=K;++j){
            dp[i][j][0]=dp[i-1][j][0];
            dp[i][j][1]=dp[i-1][j][1];
            // 当前这列打多少个砖块
            for(int k=cur[i];k<=n;++k){
                if(!c[i][k] && j-tot[i][k]>=0) {
                    dp[i][j][0]=max(dp[i][j][0],dp[i-1][j-tot[i][k]][0]+sum[i][k][1]);
                    dp[i][j][0]=max(dp[i][j][0],dp[i-1][j-tot[i][k]][1]+sum[i][k][0]);
                    dp[i][j][1]=max(dp[i][j][1],dp[i-1][j-tot[i][k]][1]+sum[i][k][1]);
                }
            }
        }
    }
    printf("%lld\n",dp[m][K][0]+ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值