Problem
T1
翻转反转后的字符串得到原串,与污染过的原串比较即可得到被污染的字符。
T2
dfs 枚举选 \(k\) 个数的方案,对于每种方案求能组成的数字个数,累加即可。
T3
恶♂趣♂味♂ \(dp\)。
首先因为数据范围过大,所以先要进行离散化。
离散化的时候可以顺便求出 \(ps_i\)、\(pt_i\) 和 \(pg_i\),
分别表示在 \(i\) 之前的白 / 黑 / 黑白区间的最大右端点。
然后令 \(fs_i\) 为有以 \(i\) 为右端点的白区间时的方案数;
\(ft_i\) 为有以 \(i\) 为右端点的黑区间时的方案数;
\(fg_i\) 为有以 \(i\) 为右端点的白区间时的方案数;
\(ss_i\)、\(st_i\)、\(sg_i\) 分别是上面三者的前缀和。
接着考虑转移。
考虑白区间,它一定从黑 / 黑白区间转移而来,从黑转移而来的方案数即为 \(st_i-st_{pt_i-1}\)。黑白区间同理。
黑区间的转移路径与白区间是类似的。
考虑黑白区间,它可以从三个类型的区间转移而来,且还要加上当前区间的方案数,所以总方案数应为 \((fs_{i-1}+ft_{i-1}+fg_{i-1}) \times (2^{a_i-a_{i-1}+1}-2)\)。
记得更新 \(ps_i\)、\(pt_i\) 和三个前缀和。
这样转移过后,答案即为 \(fs_n+ft_n+fg_n\)。
于是这道题就做完了,时间复杂度 \(O(n)\)。
代码(还是挺长的):
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MOD=1000000007;
int k,n,m;
int tot,a[400031];
int ls[400031],rs[400031],lt[400031],rt[400031];
int ps[400031],pt[400031];
int fs[400031],ft[400031],fg[400031];
int ss[400031],st[400031],sg[400031];
int qpow(int b,int p){
if(p==0) return 1;
if(p==1) return b%MOD;
int t=qpow(b,p/2);
if(p%2==0) return t*t%MOD;
else return t*t%MOD*b%MOD;
}
int mod(int x){ return (x>=MOD?x-MOD:x); }
signed main(){
cin>>k>>n>>m;
for(int i=1;i<=n;i++){
cin>>ls[i]>>rs[i];
a[++tot]=ls[i]-1,a[++tot]=rs[i];
}
for(int i=1;i<=m;i++){
cin>>lt[i]>>rt[i];
a[++tot]=lt[i]-1,a[++tot]=rt[i];
}
a[++tot]=0,a[++tot]=k;
sort(a+1,a+tot+1);
tot=unique(a+1,a+tot+1)-a-1;
for(int i=1;i<=n;i++){
int L=lower_bound(a+1,a+tot+1,ls[i]-1)-a;
int R=lower_bound(a+1,a+tot+1,rs[i])-a;
ps[R]=max(ps[R],L+1);
}
for(int i=1;i<=m;i++){
int L=lower_bound(a+1,a+tot+1,lt[i]-1)-a;
int R=lower_bound(a+1,a+tot+1,rt[i])-a;
pt[R]=max(pt[R],L+1);
}
fg[1]=sg[1]=1,ps[1]=pt[1]=1;
for(int i=2;i<=tot;i++){
ps[i]=max(ps[i],ps[i-1]);
pt[i]=max(pt[i],pt[i-1]);
fs[i]+=mod(st[i-1]-st[pt[i]-1]+MOD);
fs[i]+=mod(sg[i-1]-sg[pt[i]-1]+MOD);
fs[i]=mod(fs[i]);
ft[i]+=mod(ss[i-1]-ss[ps[i]-1]+MOD);
ft[i]+=mod(sg[i-1]-sg[ps[i]-1]+MOD);
ft[i]=mod(ft[i]);
fg[i]=mod(fs[i-1]+mod(ft[i-1]+fg[i-1]))*(qpow(2,a[i]-a[i-1])-2+MOD)%MOD;
ss[i]=mod(ss[i-1]+fs[i]);
st[i]=mod(st[i-1]+ft[i]);
sg[i]=mod(sg[i-1]+fg[i]);
}
cout<<mod(fs[tot]+mod(ft[tot]+fg[tot]));
return 0;
}
T4
显然题目是要我们求最长路。跑一遍魔改 SPFA 即可。
但是本题数据很大,于是我们采用高精度的思想,将答案的每一位都存入数组中输出。
我们设定一个阈值 \(S=30\),下标 \(>S\) 的是整数部分,\(<S\) 的是小数部分。
然后将答案求出存入数组后,进行进位、四舍五入、再进位、去前导零后输出即可。
需要注意的是,我们 SPFA 求的是 \(1\) 到各点的最长路,但我们要求的是各点到 \(1\) 的最长路,因此我们需要将求出的每个最长路取反。
同时,因为方向反了,于是答案数组应当每次累加 \(a_i\)(即 \(\dfrac{a_i}{10^z} \times 10^z\))。
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,ans[100031];
int a[100031],dis[100031];
struct EdgeInfo{
int to,w;
};
struct NodeMessage{
int pos,tme;
};
vector<EdgeInfo> G[200031];
queue<NodeMessage> q;
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1,x,y,z;i<=m;i++){
cin>>x>>y>>z;
G[x].push_back((EdgeInfo){y,z});
}
memset(dis,-0x3f3f3f3f,sizeof(dis));
q.push((NodeMessage){1,0}),dis[1]=0;
while(!q.empty()){
int x=q.front().pos; q.pop();
for(auto i:G[x]){
if(dis[x]+i.w>dis[i.to]){
dis[i.to]=dis[x]+i.w;
q.push((NodeMessage){i.to,dis[x]+i.w});
}
}
}
for(int i=1;i<=n;i++) dis[i]=-dis[i];
for(int i=1;i<=n;i++)
if(dis[i]+30>=0) ans[dis[i]+30]+=a[i];
for(int i=1;i<=10040;i++) ans[i+1]+=ans[i]/10,ans[i]%=10;
if(ans[23]>=5) ans[24]++;
for(int i=1;i<=10040;i++) ans[i+1]+=ans[i]/10,ans[i]%=10;
int now=100004;
while(!ans[now]&&now>30) now--;
for(int i=now;i>=30;i--) cout<<ans[i];
cout<<'.';
for(int i=29;i>=24;i--) cout<<ans[i];
return 0;
}