2023/7/5--个人总结

7/2以前:牛客,cf补题,在洛谷刷了一下简单的dp

C. Strong Password

题目大意:(字符串均由0~9组成)给字符串s,密码长度m,密码区间字符串l,r,l[i],r[i]表示第i位密码的范围,求是否存在一个密码串不是s的子串,存在输出YES,否则输出NO。 因为密码串的每一位都在0~9之间,所以直接枚举第i位的范围(l[i]~r[i]),在s中查找,若找到则存储下标(下一次从该下标后面开始查找),若没有找到则输出YES。因为若第i位在s中出现,我们肯定希望剩下的不在s中出现,所以我们枚举i的范围,要存储每一次找到的下标中最靠后的下标。

void solve() {
    string s, l, r;
    cin >> s;
    int m;
    cin >> m;
    cin >> l >> r;
    int k = 0;
    for(int i = 0; i < m; i++) {
        int k1, maxn = 0;
        for(char j = l[i]; j <= r[i]; j++) {
            k1 = k;
            while(k1 < s.size() && j != s[k1])
                k1++;
            if(k1 == s.size()) {
                cout << "YES" << endl;
                return;
            }
            if(k1 > maxn)
                maxn = k1;
        }
        k = maxn + 1;
    }
    cout << "NO" << endl;
}

7/3-7/4:树状数组,线段树(简单)

抄了点模板:

树状数组:

区间修改+区间查询:
ll t1[110005],n,t2[110005];
ll a[100005];

ll lowbit(ll x) {
     return x & -x;
}

void add(ll p,ll k) {     //单点修改
    for (ll i = p; i <= n; i += lowbit(i))
        t1[i] += k, t2[i] += k * p;
}

ll ask(ll p) {    //求p位置的前缀和
    ll sum = 0;
    ll i = p;
    while (i) {
        sum += (p + 1) * t1[i] - t2[i];
        i -= lowbit(i);
    }
    return sum;
}

ll range_ask(ll L,ll R) {    //区间求和
    return g_sum(R) - g_sum(L - 1);
}

void range_add(ll l,ll r,ll k) {   //区间修改
    add(l, k);
    add(r + 1, -k);
}


二维:
const int N = 2050;
int n, m;
ll a[N][N], b[N][N], c[N][N], d[N][N];

void add(int x, int y, ll v) {
    for (int i = x; i <= n; i += i & -i) {
        for (int j = y; j <= m; j += j & -j) {
            a[i][j] += v;
            b[i][j] += (x - 1) * v;
            c[i][j] += (y - 1) * v;
            d[i][j] += (x - 1) * (y - 1) * v;
        }
    }
}

void range_add(int x1,int y1,int x2,int y2,ll v) {
    add(x1, y1, v);
    add(x1, y2 + 1, -v);
    add(x2 + 1, y1, -v);
    add(x2 + 1, y2 + 1, v);
}

ll query(int x, int y) {
    ll ret = 0;
    for (int i = x; i > 0; i -= i & -i) {
        for (int j = y; j > 0; j -= j & -j) {
            ret += x * y * a[i][j]
                   - y * b[i][j]
                   - x * c[i][j]
                   + d[i][j];
        }
    }
    return ret;
}

ll range_query(int x1,int y1,int x2,int y2,ll v) {
    return query(x2, y2)
           - query(x1 - 1, y2)
           - query(x2, y1 - 1)
           + query(x1 - 1, y1 - 1)
}

线段树:

定义:
#define maxn 100007  //元素总个数  
#define ls l,m,rt<<1  
#define rs m+1,r,rt<<1|1  
int Sum[maxn<<2],mark[maxn<<2];//Sum求和,mark为懒惰标记   
int A[maxn],n;//存原数组数据下标[1,n]  

建树:
//PushUp函数更新节点信息 ,这里是求和  
void PushUp(int rt){Sum[rt]=Sum[rt<<1]+Sum[rt<<1|1];}  
//Build函数建树   
void Build(int l,int r,int rt){ //l,r表示当前节点区间,rt表示当前节点编号  
    if(l==r) {//若到达叶节点   
        Sum[rt]=A[l];//储存数组值   
        return;  
    }  
    int m=(l+r)>>1;  
    //左右递归   
    Build(l,m,rt<<1);  
    Build(m+1,r,rt<<1|1);  
    //更新信息   
    PushUp(rt);  
}  

点修改:(+C)
void Update(int L,int C,int l,int r,int rt){//l,r表示当前节点区间,rt表示当前节点编号  
    if(l==r){//到叶节点,修改   
        Sum[rt]+=C;  
        return;  
    }  
    int m=(l+r)>>1;  
    //根据条件判断往左子树调用还是往右   
    if(L <= m) Update(L,C,l,m,rt<<1);  
    else       Update(L,C,m+1,r,rt<<1|1);  
    PushUp(rt);//子节点更新了,所以本节点也需要更新信息   
}   

区间修改:(+C)
void Update(int L,int R,int C,int l,int r,int rt){//L,R表示操作区间,l,r表示当前节点区间,rt表示当前节点编号   
    if(L <= l && r <= R){//如果本区间完全在操作区间[L,R]以内   
        Sum[rt]+=C*(r-l+1);//更新数字和,向上保持正确  
        mark[rt]+=C;//增加Add标记,表示本区间的Sum正确,子区间的Sum仍需要根据Add的值来调整  
        return ;   
    }  
    int m=(l+r)>>1;  
    PushDown(rt,m-l+1,r-m);//下推标记  
    //这里判断左右子树跟[L,R]有无交集,有交集才递归   
    if(L <= m) Update(L,R,C,l,m,rt<<1);  
    if(R >  m) Update(L,R,C,m+1,r,rt<<1|1);   
    PushUp(rt);//更新本节点信息   
}   

区间查询:(和)
void PushDown(int rt,int ln,int rn){   //下推标记的函数
    //ln,rn为左子树,右子树的数字数量。   
    if(mark[rt]){  
        //下推标记   
        mark[rt<<1]+=mark[rt];  
        mark[rt<<1|1]+=mark[rt];  
        //修改子节点的Sum使之与对应的Add相对应   
        Sum[rt<<1]+=mark[rt]*ln;  
        Sum[rt<<1|1]+=mark[rt]*rn;  
        //清除本节点标记   
        mark[rt]=0;  
    }  
}  
//查询函数
int Query(int L,int R,int l,int r,int rt){//L,R表示操作区间,l,r表示当前节点区间,rt表示当前节点编号  
    if(L <= l && r <= R){  
        //在区间内,直接返回   
        return Sum[rt];  
    }  
    int m=(l+r)>>1;  
    //下推标记,否则Sum可能不正确  
    PushDown(rt,m-l+1,r-m);   
      
    //累计答案  
    int ANS=0;  
    if(L <= m) ANS+=Query(L,R,l,m,rt<<1);  
    if(R >  m) ANS+=Query(L,R,m+1,r,rt<<1|1);  
    return ANS;  
}   

函数调用:
//建树   
Build(1,n,1);   
//点修改  
Update(L,C,1,n,1);  
//区间修改   
Update(L,R,C,1,n,1);  
//区间查询   
int ANS=Query(L,R,1,n,1);  


讲解:https://www.cnblogs.com/AC-King/p/7789013.html

算是一个初步了解,后面再加强。

Mayor's posters - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) --- 线段树加离散化

由于区间数据范围太大,所以需要用到离散化,离散化后就是线段树的染色问题。设该区间未染色为0,染色:只有一种颜色>0(颜色编号),多种颜色为-1

int t[1000005];      //线段树
int mark[1000005];   //懒惰标记
int co[100005];     //标记颜色是否已添加(最后查询时)

void gex(int x) {    //更新
    if (t[x << 1] == t[x << 1 | 1]) {
        t[x] = t[x << 1];
    }
    else
        t[x] = -1;
}

void pd(int rt, int ln, int rn) {   //下推标记
    if (mark[rt]) {
        mark[rt << 1] = mark[rt];
        mark[rt << 1 | 1] = mark[rt];

        t[rt << 1] = mark[rt];
        t[rt << 1 | 1] = mark[rt];

        mark[rt] = 0;
    }
}

void add(int l, int r, int L, int R, int c, int rt) {  //染色
    if (L <= l && r <= R) {
        t[rt] = c;
        mark[rt] = c;
        return;
    }
    int m = (r - l) / 2 + l;
    pd(rt, m - l + 1, r - m);
    if (L <= m)
        add(l, m, L, R, c, rt << 1);
    if (R > m)
        add(m + 1, r, L, R, c, rt << 1 | 1);
    gex(rt);
}
int ans;

void up(int rt) {        //查询
    if (t[rt] > 0) {
        if (co[t[rt]] == 0) {
            ans++;
            co[t[rt]] = 1;
        }
        return;
    }
    if (t[rt] == 0) {
        return;
    }
    if (t[rt] == -1) {
        up(rt << 1);
        up(rt << 1 | 1);
    }
}

typedef struct node {
    int l, r;
} node;

vector<int>pos;

void solve() {
    int n;
    cin >> n;
    int l, r;
    queue<node>q;
    int cnt = 0;
    while (n--) {
        cin >> l >> r;
        pos.push_back(l);
        pos.push_back(r);
        q.push(node{ l, r });
    }
    //离散化
    sort(pos.begin(), pos.end());
    int len = pos.size();
    for (int i = 1; i < len; i++) {
        if (pos[i] - pos[i - 1] != 1)pos.push_back(pos[i] - 1);
    }
    sort(pos.begin(), pos.end());
    pos.erase(unique(pos.begin(), pos.end()), pos.end()); //去重(必须先排序)
    //
    //构造树
    while (!q.empty()) {
        node p = q.front();
        q.pop();
        p.l = lower_bound(pos.begin(), pos.end(), p.l) - pos.begin() + 1;
        p.r = lower_bound(pos.begin(), pos.end(), p.r) - pos.begin() + 1;
        add(1, pos.size(), p.l, p.r, ++cnt, 1);
    }
    up(1);
    cout << ans << endl;
    //清空
    for (int j = 0; j <= 10000; j++)
        co[j] = 0;
    ans = 0;
    pos.clear();
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int T = 1;
    cin >> T;
    while (T--) {
        solve();
    }
    return 0;
}

7/4-7/5:在洛谷上刷数据结构中的题。

P1364 医院设置

换根dp
考虑两种情况:
原来在v的子树中的数,离v比u少1,所以答案集体-1.
原来不在v子树中的数,离v比u多1,所以答案集体+1.
f[v] = f[u] + (sz[1] - sz[v]) - sz[v];
sz[1]为全树权值和,(sz[1] - sz[v])表示原来不在v子树中的+1, -sz[v]表示在的-1
用dfs算出f[1]和初始化sz,然后dp
const int N = 105;
vector<int>g[N];   //存储树
int sz[N];     //子树权值和
int w[N];      //权值
int f[N];       //dp答案
int ans;        //最终答案

void dfs(int u, int fa = 0, int dep = 0) {
    sz[u] = w[u];
    for(auto v : g[u]) {
        if(v != fa) {
            dfs(v, u, dep + 1);
            sz[u] += sz[v];
        }
    }
    f[1] += w[u] * dep;
}

void dp(int u, int fa = 0) {
    for(auto v : g[u]) {
        if(v != fa) {
            f[v] = f[u] + sz[1] - (sz[v] << 1);
            dp(v, u);    //换根
        }
    }
    ans = min(ans, f[u]);
}

void solve() {
    int n;
    cin >> n;
    rep(i, 1, n) {
        int u, v;
        cin >> w[i] >> u >> v;
        if(u) {
            g[i].push_back(u);
            g[u].push_back(i);
        }
        if(v) {
            g[i].push_back(v);
            g[v].push_back(i);
        }
    }
    ans = 1e7;
    dfs(1);
    dp(1);
    cout << ans << endl;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    int T;
    T = 1;
    //  cin >> T;
    while (T--) {
        solve();
    }
    return 0;
}

这道题因为数据范围小,也可以用f'loyd算出每一个点之间的最短路径,然后枚举每一个点为医院选址的情况,最后取最小值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

akb000

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值