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:在洛谷上刷数据结构中的题。
换根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算出每一个点之间的最短路径,然后枚举每一个点为医院选址的情况,最后取最小值。