背景:这两场比赛笔者并没有打,这两天补完之后写一篇总结(前三题比较水我就没补,441补了DEF,442补了DF,一共5道题目)
这道题非常水,我们注意到L非常小,而每个点的出度至多为4,所以我们从1号点开始,每个点暴力跑10条边,最坏的情况下每个点的出度均为4,所以时间复杂度只有4^l,完全可以跑过去
我们维护一个vis数组,vis[i]=1说明这个点满足题目约束,然后我们dfs时传一个目前的边权和走的边数,如果走了10步后这个点的边权和在[S,T]内那么它是一个合法的点,注意只要有一次合法判定,这个点就是合法的
具体代码如下,其实也不难实现:
cpp
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define _for(i,a,b) for(int i = (a) ; i <= (b) ; i ++)
#define for_(i,a,b) for(int i = (a) ; i >= (b) ; i --)
#define ls k << 1
#define rs k << 1 | 1
#define inf 0x3f3f3f3f
int n,m,s,t,l;
const int maxn = 2e5 + 10;
struct node{
int to,val;
};
vector<node>v[maxn];
set<int>st;//储存合法点
int tag[maxn];
void dfs1(int u,int dep,int val){
if(dep == l) {
tag[u] |= (val >= s && val <= t);
//合法一次就可以
return;
}
for(auto itr:v[u]){
dfs1(itr.to,dep + 1,val + itr.val);
}
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin >> n >> m >>l >> s >> t;
_for(i , 1 , m){
int x,y,z;
cin >> x >> y >> z;
v[x].push_back({y,z});
}
dfs1(1,0,0);
_for(i , 1 , n) {
if(tag[i]) st.insert(i);//合法
}
for(auto itr:st) cout << itr << ' ';
return 0;
}
AT_abc441_e [ABC441E] A > B substring
(这题我做的时候是黄色,为啥能升绿)
假设一个区间[l,r]满足A>B,那么会有如下式子
改写成前缀和的形式
移项
所以发现了吗,我们只需要找若干对二元组(l,r)满足这个式子的约束即可,令tmp[i]=suma[i]-sumb[i],我们只需要找到所有(l,r)使得tmp[r]>tmp[l],那么就是一个求逆序对的板子,上树状数组即可
注意l的范围是[0,r-1],不要遗漏
然后这个数tmp[i]可能是负数,我们需要离散化一下,代码有一些细节,具体如下
cpp
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define _for(i,a,b) for(int i = (a) ; i <= (b) ; i ++)
#define for_(i,a,b) for(int i = (a) ; i >= (b) ; i --)
const int maxn = 2e6 + 190;
string s;
int suma[maxn ],sumb[maxn ],tmp[maxn],t[maxn];
int n,ans;
int iup = 3e5 + 10;
int lowbit(int x){return x & (- x);}
int query(int x){
int num = 0;
for(int i = x ; i >= 1 ; i -= lowbit(i)) num += t[i];
return num;
}
void update(int idx){
for(int i = idx ; i <= 1000000 ; i += lowbit(i)) t[i] ++;
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin >> n;
cin >> s;
s = '_' + s;
_for(i , 1 , n) {
suma[i] = suma[i - 1];
sumb[i] = sumb[i - 1];
if(s[i] == 'A' ) suma[i] ++;
else if(s[i] == 'B') sumb[i] ++;
tmp[i] = suma[i] - sumb[i];
}
update(n + 1);//0位置
_for(i , 1 , n){
ans += query(tmp[i] + n);
update(tmp[i] + n + 1);
}
cout << ans;
return 0;
}
(题外:这个题笔者做的时候是半夜,可能脑子比较昏,我想着把数组开大一点避免离散化的时候RE,又怕MLE,于是我把t数组开了3e6,别的想都没想直接maxn/10,以为n是1e5,我调10分钟也没看出来哪里RE了,最后考虑MLE的时候又一次看了题目,才发现n5e5,遂过)
AT_abc441_f [ABC441F] Must Buy
(密码币的这个破题为啥没有中文翻译)
我们设总体背包中,强制包含一个物品,所得到的最大价值为x,设总体背包中,强制不包含一个物品所得到的最大价值为y,不强制但也不排除这个物品,也就是没有任何选择约束下的01背包可以获得的最大价值是v,我们分析一下
1):物品是A类
那么最优方案中必须包含它,强制选它一定可以构造出最优方案,并且如果没有它最优方案的价值就会变小,所以x>y且x=v
2):物品是B类
那么最优方案中可以包含它,也可以不包含,那么包含它不包含它都可以构造出最优方案,所以x=y=z
3):物品是C类
那就是选了它就无法构造出最优方案,也无需关心x,y的大小关系,x<z就可以判
然后我们不能每个数都维护xi和yi,我们考虑如果不选i的话,要么选[1,i-1]的物品,要么选[i+1,n]的物品,也可以都选,我们只需要维护一个前缀最优和一个后缀最优,然后拼接即可,具体如下
我们维护dp1[i][j]表示物品[1,i]中,重量为j的最大价值,dp2[i][j]表示物品[i,n],重量为j的最大价值
这两个数组的维护就是01背包的模板(那个第一次学完就没用过的二维01背包板子)
然后我们每个物品就可以O(m)的时间内找到xi和yi,对于xi,我们先给背包塞w[i]的重量,得到val[i]的价值,然后我们再枚举j,即分给[1,i-1]的重量,同时就能计算出[i+1,n]的最大价值,即
dp2[m-w[i]-j],然后把这三部分的价值加起来和原本xi取max即可,yi也差不多,代码如下,还是很好写的
cpp
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define _for(i,a,b) for(int i = (a) ; i <= (b) ; i ++)
#define for_(i,a,b) for(int i = (a) ; i >= (b) ; i --)
const int maxn = 1e3 + 10;
int dp1[maxn][50010];
int dp2[maxn][50010];
int w[maxn],val[maxn];
int n,m;
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin >> n >> m;
_for(i , 1 , n) cin >> w[i] >> val[i];
_for(i , 1 , n){
_for(j , 0 , m){
dp1[i][j] = dp1[i - 1][j];
if(j >= w[i]) dp1[i][j] = max(dp1[i][j],dp1[i - 1][j - w[i]] + val[i]);
}
}
for_(i , n , 1){
_for(j , 0 , m){
dp2[i][j] = dp2[i + 1][j];
if(j >= w[i]) dp2[i][j] = max(dp2[i][j],dp2[i + 1][j - w[i]] + val[i]);
}
}
int MAX = dp1[n][m];//无限制下最优
_for(i , 1 , n){
int tmp1 = 0,tmp2 = 0;
//tmp1不包含,tmp2一定包含
_for(j , 0 , m){
tmp1 = max(tmp1,dp1[i - 1][j] + dp2[i + 1][m - j]);
if(j >= w[i]) tmp2 = max(tmp2,dp1[i - 1][j - w[i]] + val[i] + dp2[i + 1][m - j]);
}
if(tmp2 == MAX){
if(tmp1 == MAX) cout << 'B';
else cout << 'A';
}
else cout << 'C';
}
return 0;
}
(这场的G至今没调出来,神秘错误)
442的D是有史以来最水D题,维护一个前缀和即可,我认为没有在这里介绍的必要,E题据同学说是极角排序(我不会),想到维护三角函数值但是至今没调出来(神秘),所以我这里只分享一下F题
AT_abc442_f [ABC442F] Diagonal Separation 2
(这个题我写了题解,点击这里获得更好观感)
这里我就不再赘述了,我简单说一个优化dp的常见套路,就是这种一维转移方向固定(比如i->i-1),另一维的转移是一个区间(比如这个题就是[j,n])这个时候我们就可以考虑使用前缀和或者后缀优化,也是比较常见的优化套路了(其实最开始没调过,换了st表写还是没过,最后发现是mn[n]没有初始赋值)
(感谢您的观看,点个赞再走呗~)