当前位置:网站首页>2021-09-11 训练场补题
2021-09-11 训练场补题
2022-06-11 21:18:00 【dplovetree】
C - Family View
题意:
给你 n n n个模式串,模式串中只有小写字母,模式串长度之和 l e n < = 1 e 6 len<=1e6 len<=1e6,然后给你一个匹配串 l e n < = 1 e 6 len<=1e6 len<=1e6,匹配串由全字符集组成。要求将匹配串中的所有和模式串匹配的子段替换成 ‘*’ ;
思路:
优化AC自动机;普通的AC自动机在匹配的时候会暴力跳fail指针;也有建fail 树来统计每个匹配串在文本串中出现的次数的优化。但是这道题我们只用在匹配的地方加个标记,记录长度即可,想象原本跳fail的过程,对于自动机上每个fail节点,我们要对他的flag取max,然后加标记,那么我们在建fail边的时候,直接取max就好了,这样就不用跳fail边了。那么我们只要遍历自动机,如果当前文本串的字符不是字母,那么我们直接回到自动机的元节点即可。
结构体数组不要赋初值!默认会把全数组赋值,然后就MLE了。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=7e5+4;
int t,n;
struct node{
int son[26],fail=0,flag;
}trie[maxn];
int p[maxn];
int tot=1;
queue<int>q;
char s[maxn];
void init(){
tot=0;
for(int j=0;j<26;j++){
trie[tot].son[j]=0;
}
trie[tot].fail=0;
trie[tot].flag=0;
}
void insert(char *s){
int len=strlen(s),u=0;
for(int i=0;i<len;i++){
int v=s[i]-'a';
if(!trie[u].son[v]){
trie[u].son[v]=++tot;
for(int j=0;j<26;j++){
trie[tot].son[j]=0;
}
trie[tot].fail=0;
trie[tot].flag=0;
}
u=trie[u].son[v];
}
trie[u].flag=max(trie[u].flag,len);
}
void getfail(){
for(int i=0;i<26;i++)
if(trie[0].son[i])q.push(trie[0].son[i]);
while(!q.empty()){
int u=q.front();q.pop();
trie[u].flag=max(trie[u].flag,trie[trie[u].fail].flag);
for(int i=0;i<26;i++){
int v=trie[u].son[i];
int x=trie[u].fail;
if(!v){
trie[u].son[i]=trie[x].son[i];
}else{
trie[v].fail=trie[x].son[i];
q.push(v);
}
}
}
}
void query(char *s){
int u=0,len=strlen(s);
for(int i=0;i<len;i++)p[i]=0;
for(int i=0;i<len;i++){
int v=-1;
if(s[i]>='A'&&s[i]<='Z')v=s[i]-'A';
if(s[i]>='a'&&s[i]<='z')v=s[i]-'a';
if(v==-1)u=0;
else u=trie[u].son[v];
if(trie[u].flag){
p[i+1]--;
p[i-trie[u].flag+1]++;
}
}
int res=0;
for(int i=0;i<len;i++){
res+=p[i];
if(res==0)putchar(s[i]);
else putchar('*');
}
printf("\n");
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d",&n);
init();
for(int i=1;i<=n;i++){
scanf("%s",s);
insert(s);
}
getfail();
getchar();
scanf("%[^\n]",s);
query(s);
}
return 0;
}
//D I K L
D - Tea
题意:
懒得写
思路:
分类讨论
看代码应该看得懂
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e6+4;
int t,n;
ll l,r;
ll ans=0;
int main(){
while(cin>>l>>r){
ans=1e18;
ll res=r-l;
if(res<=3)ans=2;
else ans=(res-3+1)/2+2;
if(r<=2)ans=1;
if(r<=1)ans=0;
if(l==0)ans=min(ans,(r+1)/2);
cout<<ans<<endl;
}
return 0;
}
//D I K L
I - Tower Defence
题意:
给你 n n n个节点的带边权的树( n < = 1 e 5 n<=1e5 n<=1e5),定义每次删除一个边,对答案的贡献是分开的两颗子树中直径的较大值。求分别删除每条边对答案的贡献和。
思路:
我们分成两种情况来考虑:
如果删除的边不是整棵树的直径上的边,那么对答案的贡献就还是整棵树的直径。
如果删除的边不是整棵树的直径,我们对两边子树的直径取较大值。
我们可以比较简单地证明出,删掉一条边之后,子树的直径一定是由原直径的端点出发的。(如果不是从端点出发的最长链,那么他就会取代现在端点的这条链,加入原直径)。
那么我们就可以预处理出删除直径上的一条边后,子树的最大直径,即对于每个点,求他不经过直径最远能延伸多少,然后左右分别做一次DP, d p [ i ] = m a x ( l e n , n o w + c n t ) dp[i]=max(len,now+cnt) dp[i]=max(len,now+cnt),最后再跑一次直径取较大值加到答案中即可。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll n,t;
struct node{
ll to,w;
};
vector<node>v[100040];
ll dep[100040];
bool vis[100040];
int root,root2;
ll sum=0;
ll ans=0;
ll res=0;
ll dp1[100040];
ll dp2[100040];
void dfs1(int x,int pre){
if(dep[x]>dep[root])root=x;
for(int i=0;i<v[x].size();i++){
if(v[x][i].to==pre)continue;
dep[v[x][i].to]=dep[x]+v[x][i].w;
dfs1(v[x][i].to,x);
}
}
void dfs2(int x){
vis[x]=1;res++;
for(int i=0;i<v[x].size();i++){
if(dep[x]==dep[v[x][i].to]+v[x][i].w){
dfs2(v[x][i].to);
}
}
}
ll cnt=0;
ll now=0;
ll lenn=0;
void dfs4(int x,int pre,ll d){
cnt=max(cnt,d);
for(int i=0;i<v[x].size();i++){
if(v[x][i].to==pre||vis[v[x][i].to])continue;
dfs4(v[x][i].to,x,d+v[x][i].w);
}
}
int f=0;
void dfs3(int x,int pre){
cnt=0;
if(f==0&&x!=root2){
dfs4(x,0,0);
dp1[x]=max(lenn,now+cnt);
lenn=max(lenn,now+cnt);
}
if(f==1&&x!=root){
dfs4(x,0,0);
dp2[x]=max(lenn,now+cnt);
lenn=max(lenn,now+cnt);
}
for(int i=0;i<v[x].size();i++){
if(v[x][i].to==pre||vis[v[x][i].to]==0)continue;
now+=v[x][i].w;
dfs3(v[x][i].to,x);
}
}
void dfs5(int x,int pre){
if(x!=root2){
ans+=max(dp1[pre],dp2[x]);
}
for(int i=0;i<v[x].size();i++){
if(v[x][i].to==pre||vis[v[x][i].to]==0)continue;
dfs5(v[x][i].to,x);
}
}
int main(){
scanf("%lld",&t);
while(t--){
scanf("%lld",&n);
for(int i=1;i<n;i++){
ll a,b,c;
scanf("%lld%lld%lld",&a,&b,&c);
v[a].push_back({
b,c});
v[b].push_back({
a,c});
}
for(int i=1;i<=n;i++)vis[i]=0,dp1[i]=0,dp2[i]=0;
root=0;
dep[0]=0;
dep[1]=0;
dfs1(1,0);
root2=root;
dep[root]=0;
dfs1(root,0);
sum=dep[root];
ans=0;res=0;
dfs2(root);
ans=1LL*sum*(n-res);
now=0;lenn=0;
f=0;
dfs3(root2,0);
f=1;
now=0;lenn=0;
dfs3(root,0);
dfs5(root2,0);
printf("%lld\n",ans);
for(int i=1;i<=n;i++)v[i].clear();
}
return 0;
}
//K L
K - Barricade
题意:
给你 n n n个点 m m m条边的图,你需要堵住从1到 n n n的所有最短路,堵住每条边会有一个代价。求堵住最短路的最小代价。
思路:
比赛当时,想了一个有的没的DP转移,后来发现不对。
然后其实就是一个网络流裸题,作为网络流选手,没看出来是不应该的,需要自己反思。
就先把最短路扣出来,堵住这些路就是把点1和点 n n n,分成两部分,典型的最小割。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int t,n,m;
struct node{
int to,w;
};
vector<node>v[1004];
int dis1[1004],dis2[1004];
queue<int>q;
struct edge{
int to,cap,rev;
};
vector<edge>mp[1004];
int level[1004];//顶点到源点的距离标号
int iter[1004]; //当前弧,在其之前的边已经没有用了
void add(int from,int to,int cap){
mp[from].push_back({
to,cap,mp[to].size()});
mp[to].push_back({
from,0,mp[from].size()-1});
}
void bfs(int s){
memset(level,-1,sizeof(level));
queue<int>q;
level[s]=0;
q.push(s);
while(!q.empty()){
int v=q.front();q.pop();
for(int i=0;i<mp[v].size();i++){
edge &e=mp[v][i];
if(e.cap>0&&level[e.to]<0){
level[e.to]=level[v]+1;
q.push(e.to);
}
}
}
}
int dfs(int v,int t,int f){
if(v==t)return f;
for(int &i=iter[v];i<mp[v].size();i++){
edge &e=mp[v][i];
if(e.cap>0&&level[v]<level[e.to]){
int d=dfs(e.to,t,min(f,e.cap));
if(d>0){
e.cap-=d;
mp[e.to][e.rev].cap+=d;
return d;
}
}
}
return 0;
}
int max_flow(int s,int t){
int flow=0;
while(1){
bfs(s);
if(level[t]<0)return flow;
memset(iter,0,sizeof(iter));
int f;
while((f=dfs(s,t,1e9))>0)flow+=f;
}
return flow;
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int a,b,c;scanf("%d%d%d",&a,&b,&c);
if(a==b)continue;
v[a].push_back({
b,c});
v[b].push_back({
a,c});
}
for(int i=1;i<=n;i++){
dis1[i]=dis2[i]=1e9;
}
dis1[1]=0;
while(!q.empty())q.pop();
q.push(1);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<v[x].size();i++){
if(dis1[v[x][i].to]>dis1[x]+1){
dis1[v[x][i].to]=dis1[x]+1;
q.push(v[x][i].to);
}
}
}
dis2[n]=0;
q.push(n);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=0;i<v[x].size();i++){
if(dis2[v[x][i].to]>dis2[x]+1){
dis2[v[x][i].to]=dis2[x]+1;
q.push(v[x][i].to);
}
}
}
for(int i=1;i<=n;i++){
for(int j=0;j<v[i].size();j++){
if(dis1[i]+dis2[v[i][j].to]+1==dis1[n]){
add(i,v[i][j].to,v[i][j].w);
}
}
}
printf("%d\n",max_flow(1,n));
for(int i=1;i<=n;i++)v[i].clear(),mp[i].clear();
}
return 0;
}
//K L
L - Eighty seven
题意:
给你 n n n个数( n < = 50 n<=50 n<=50), 每次保证不取小于等于3个数的前提下,问能否取十个数使得这十个数的和为87;
思路:
暴力背包+bitset优化
代码:
#include<bits/stdc++.h>
using namespace std;
bitset<90>s[11];
int a[51],f[51][51][51];
int n;
int solve(int x,int y,int z){
for(int i=0;i<=10;i++)s[i].reset();
s[0][0]=1;
for(int i=1;i<=n;i++){
if(i==x||i==y||i==z||a[i]>87)continue;
for(int j=10;j>0;j--){
s[j]|=s[j-1]<<a[i];
}
}
return s[10][87];
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j++){
for(int k=j;k<=n;k++){
f[i][j][k]=solve(i,j,k);
}
}
}
int m;
scanf("%d",&m);
while(m--){
int b[3];
scanf("%d%d%d",&b[0],&b[1],&b[2]);
sort(b,b+3);
if(f[b[0]][b[1]][b[2]])printf("Yes\n");
else printf("No\n");
}
}
}
边栏推荐
- BUG -- coredump使用
- Serval and Rooted Tree(CF1153D)-DP
- 重投农业,加码技术服务,拼多多底盘进一步夯实
- Tensorflow 2. X Getting Started tutorial
- Technical exchange | why should network security equipment use bypass function
- Deriving Kalman filter from probability theory
- Js 监听滚动触底加载更多_浏览器滚动触底加载更多
- 正则校验匹配[0-100]、[0-1000]之间的正整数或小数点位数限制
- Iros 2021 | new idea of laser vision fusion? Lidar intensity diagram +vpr
- LR-LINK联瑞携新品首次亮相数博会-助力新基建数据中心建设
猜你喜欢

Why should I use iwarp, roce V2, nvme of and other protocols for 100g network transmission

php pcntl_fork 创建多个子进程解析

【数据可视化】使用 Apache Superset 可视化 ClickHouse 数据

Tensorflow 2. X Getting Started tutorial
![[data visualization] Apache superset 1.2.0 tutorial (III) - detailed explanation of chart functions](/img/1f/00f2085186971198928b012a3792ea.jpg)
[data visualization] Apache superset 1.2.0 tutorial (III) - detailed explanation of chart functions

Online excel file parsing and conversion to JSON format

Docker installing MySQL

One article to show you how to understand the harmonyos application on the shelves

Iros 2021 | new idea of laser vision fusion? Lidar intensity diagram +vpr

Part II data link layer
随机推荐
2022年6月9日 16:29:41 日记
The difference between VaR and let_ The difference between let and VaR
Mysql add 新增多个新字段并指定字段位置
Release of version 5.6 of rainbow, add multiple installation methods, and optimize the topology operation experience
【C語言進階】整型在內存中的存儲
The e-sports Internet cafe uses a 2.5G network card to experience the feeling of flying!
Live broadcast with practice | 30 minutes to build WordPress website with Alibaba cloud container service and container network file system
使用 float 创建一个网页页眉、页脚、左边的内容和主要内容。
Regular check matches positive integer or decimal limit between [0-100] and [0-1000]
第一部分 物理层
可综合RTL代码设计方法和注意事项
Release of version 5.6 of rainbow, add multiple installation methods, and optimize the topology operation experience
为什么需要微服务
电竞网咖用2.5G网卡,体验飞一般的感觉!
[Game Theory - introduction]
js对返回的数据的各种数据类型进行非空判断。
Docker installing MySQL
go语言的goto语句
【数据可视化】Apache Superset 1.2.0教程 (三)—— 图表功能详解
Docker installation redis