大意:给定一个字符串,有 m 个魔法,每个魔法可以将区间 [ l i , r i ] [l_i,r_i] [li,ri]的一个字符修改成 c i c_i ci,每个魔法可以使用无限次。对于每个字符,它的价值就是对应的ASCII码,求最终字符串价值的最大值。
思路:我又又又又又…读错题了。看成了每个魔法把整个区间 [ l i , r i ] [l_i,r_i] [li,ri] 一下子全部修改成 c i c_i ci,然后想了个从小到大贪心,线段树维护的写法把B过了…然后就过不了 C,吐了。
正解:根据题目意思,对于每个魔法我们可以使用无限次,也就等价于对于区间的任意一个字符我们可以修改或者不修改,所以也就是维护区间最大值的问题。然后,就是对于某个区间如果我们已经被大的字符修改过了,那么就可以直接跳过,所以我们贪心的按字符从大到小使用魔法,对于某个修改过的区间可以直接跳过。可以用并查集维护。代码如下:
#include
#define rep(i,bbb,eee) for(int i=bbb;i<=eee;i++)
#define frep(i,bbb,eee) for(int i=bbb;i>=eee;i--)
#define mem(a,b) memset(a,b,sizeof(a))
#define inf 0x3f3f3f3f
#define pb push_back
#define AC signed
#define x first
#define y second
//#define int long long
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
const int N=10000010,M=998244353;
char s[N];
int n,m,fa[N];
vector<PII> seg[200];
int find(int x)
{
if(fa[x]!=x)return fa[x]=find(fa[x]);
return fa[x];
}
void solve()
{
cin>>n>>m;
cin>>(s+1);
rep(i,1,n)fa[i]=i;
while(m--)
{
char c[2];
int l,r;
cin>>l>>r>>c;
seg[c[0]].pb({l,r});
}
frep(i,126,33)
{
if(seg[i].size()==0)continue;
for(auto v:seg[i])
{
int l=v.x,r=v.y;
for(int k=find(r);k>=l;k=find(k))
{
s[k]=max(s[k],(char)i);
fa[k]=find(k-1);
}
}
}
int ans=0;
rep(i,1,n)ans+=s[i];
cout<<ans<<"\n";
}
AC main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int _=1;
//cin>>_;
while(_--)solve();
return 0;
}
总结:认真读题,认真读题!!! get 用并查集跳过访问过的区间。
大意:给定3*n的矩阵, − 1 e 9 < = a i , j < = 1 e 9 -1e9<=a_{i,j}<=1e9 −1e9<=ai,j<=1e9。从(1,1)处走到(3,n)处,每次只能往右或者往下走,求走的格子的数字之和>=0 的方案数。
思路:记 s u m k sum_k sumk为第k行的前缀和。通过读题我们不难发现实际上就是需要两个向下走的位置i,j 满足i<=j, 且 s u m 1 , i + s u m 2 , j − s u m 2 , i − 1 + s u m 3 , n − s u m 3 , j − 1 > = 0 sum_{1,i}+sum_{2,j}-sum_{2,i-1}+sum_{3,n}-sum_{3,j-1}>=0 sum1,i+sum2,j−sum2,i−1+sum3,n−sum3,j−1>=0
如果只有两行我们很容想到可以直接枚举从哪个位置开始往下走。所以我们可以尝试先枚举从哪一列(即枚举 i)到第二行,每次计算有多少个j满足 s u m 2 , j + s u m 3 , n − s u m 3 , j − 1 > = s u m 2 , i − 1 − s u m 1 , i sum_{2,j}+sum_{3,n}-sum_{3,j-1}>=sum_{2,i-1}-sum_{1,i} sum2,j+sum3,n−sum3,j−1>=sum2,i−1−sum1,i
即每次查询 > = s u m 2 , i − 1 − s u m 1 , i >=sum_{2,i-1}-sum_{1,i} >=sum2,i−1−sum1,i的个数,树状数组的经典应用。
并且需要保证 i<=j,所以可以从后往前更新。
数组范围太大,需要离散化。
代码如下:
#include
#define rep(i,bbb,eee) for(int i=bbb;i<=eee;i++)
#define frep(i,bbb,eee) for(int i=bbb;i>=eee;i--)
#define mem(a,b) memset(a,b,sizeof(a))
#define inf 0x3f3f3f3f
#define pb push_back
#define AC signed
#define x first
#define y second
#define int long long
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
const int N=1000010,M=1000000007;
int sum[5][N],a[5][N],n,tr[N],m;
vector<int> v;
int find(int x)
{
return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
void add(int x,int y)
{
while(x<=m)tr[x]+=y,x+=x&-x;
}
int ask(int x)
{
int res=0;
while(x)res+=tr[x],x-=x&-x;
return res;
}
void solve()
{
cin>>n;
rep(i,1,3)
rep(j,1,n)
{
cin>>a[i][j];
sum[i][j]=sum[i][j-1]+a[i][j];
}
for(int i=1;i<=n;i++)
{
v.pb(sum[2][i]+sum[3][n]-sum[3][i-1]);
v.pb(sum[2][i-1]-sum[1][i]);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
m=v.size();
int ans=0;
frep(i,n,1)
{
add(find(sum[2][i]+sum[3][n]-sum[3][i-1]),1);
int x=find(sum[2][i-1]-sum[1][i]);
ans+=ask(m)-ask(x-1);
ans%=M;
}
cout<<ans<<"\n";
}
AC main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int _=1;
//cin>>_;
while(_--)solve();
return 0;
}