2021.08.24 膜你赛
monitor
Description
监听的宇宙电波可以抽象成一个长度为 \(L\) 的小写字母组成的字符串。
同时在三体人总结出来了 \(n\) 段敏感电波的样子,每段敏感电波的长度都是 \(m\)。
现在请你实现一个程序,求出在这长度为 \(L\) 的小写字母组成的字符串中某个敏感电波第一次出现的位置(位置从 \(1\) 开始计数)。如果从头到尾,没有任何敏感电波出现,输出”\(no\)”(不带双引号).
Solution
看到多个模式串一个文本串,立马想到了AC自动机,把模板敲上之后发现处理答案的时候其实并不用根据什么fail指针去跳,看题解里提了几句AC自动机,但是没讲具体是怎么过的,根据AC自动机的原理,在对文本串进行操作的时候,是根据trie树向下找的,那我们如果当前到达了一个根节点,之前肯定到了上面的点,并且是连续依次到达的,因此只要扫一遍当前点是否是根节点,是的话直接输出开始点的位置,也就是 now-m+2
,其实复杂是跟KMP本质上都是 \(O(nl)\) 的,但是KMP需要跑满 \(n\) 次,我的代码很大概率上是跑不满的,因为只要确定了就直接听了,观察数据点也没有需要跑 \(10^9\) 的地方,因此就过了这道题。可能还能有更优的做法,但是没有去想。当时以为是 \(O(ml)\) 的,\(m\) 相当于是个常数。所以没注意。
Code
/*
* @Author: smyslenny
* @Date: 2021.08.23
* @Title:
* @Main idea: 感觉是 AC自动机板子题
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>
#define ll long long
#define INF 0x3f3f3f3f
#define orz cout<<"LKP AK IOI\n"
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)>(b)?(a):(b)
using namespace std;
const int mod=998244353;
const int M=1e5+5;
int n,m,l,Ans=INF,js,pos[M];
char S[M][30],T[M];
int read()
{
int x=0,y=1;
char c=getchar();
while(c<'0' || c>'9') {if(c=='-') y=0;c=getchar();}
while(c>='0' && c<='9') { x=x*10+(c^48);c=getchar();}
return y?x:-x;
}struct tr{
int ch[26],fail,cnt;
}
void insert(int x)
{
int u=0;
for(int i=0;i<m;i++)
{
int num=S[x][i]-'a';
if(!tree[u].ch[num]) tree[u].ch[num]=++js;
u=tree[u].ch[num];
}
tree[u].cnt++;
pos[x]=u;
}
int fg;
int main()
{
// freopen("monitor.in","r",stdin);
// freopen("monitor.out","w",stdout);
l=read(),n=read(),m=read();
for(int i=1;i<=n;i++)//插入
{
scanf("%s",S[i]);
insert(i);
}
scanf("%s",T);
int u=0;
for(int i=0;i<l;i++)
{
int x=T[i]-'a';
u=tree[u].ch[x];
for(int j=1;j<=n;j++)
if(u==pos[j])
{
printf("%d\n",i-m+2);
fg=1;
break;
}
if(fg) break;
}
if(!fg) printf("no\n");
return 0;
}
/*
11 3 3
aba
cba
abc
aaabbabzabz
14 3 4
abac
ccde
abcd
ababccdefabcdg
*/
lab
Description
沿着着曲率驱动的思路,\(R\) 君开发出了时间旅行传送门。
\(R\) 君将 \(n-1\) 个时间旅行传送门部署到了 \(n\) 个星球。如果只走这 \(n-1\) 个时间旅行传送门,\(R\) 君发现这 \(n\) 个星球是两两可达的(也就是一棵树)。
但是时间旅行传送门除了传送的功能外还额外有着时间旅行的功能,比如说 \((X_i, Y_i, T_i)\) 这个传送门,通过这个传送门从 $X_i $到 \(Y_i\) 时间就会增加\(T_i\)(\(T_i\) 可正可负),通过这个传送门从 \(Y_i\) 到 \(X_i\) 时间就会减少 \(T_i\)(\(T_i\) 可正可负)。
现在 \(R\) 君关心的问题是从 \(x\) 星球能不能通往 \(y\) 星球,同时时间恰好增加 \(z\)(\(z\) 可正可负)。
由于现在是一个树形的结构,所以实际上两点之间的路径唯一,所以 \(R\)君很快写了个程序计算出了这个结果。
但是随着 \(R\) 君继续部署传送门,这个问题变得复杂了起来,所以请你来帮帮忙。
各种情况分析,由于路径权值的相反性,只要有环,要么不走,要么等效完整走整数遍。假设有 \(k\) 个环,每个环的边权和为 \(x_i\),那么问题转化为不定方程\(x\times 1+b\times x2+c\times x3+....+m\times xk=T\) 是否存在整数解。
根据不定方程整数解的条件可知,gcd(x1~xm) | T 时有,否则无。
/*
* @Author: smyslenny
* @Date: 2021.08.
* @Title:
* @Main idea:两两到达,唯一路径,在提示我们什么?
* 存在环的话,是可以随意调节长度的
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>
#define ll long long
#define INF 0x3f3f3f3f
#define orz cout<<"LKP AK IOI\n"
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)>(b)?(a):(b)
using namespace std;
const int mod=998244353;
const int M=1e3+5;
int n,q;
int read()
{
int x=0,y=1;
char c=getchar();
while(c<'0' || c>'9') {if(c=='-') y=0;c=getchar();}
while(c>='0' && c<='9') { x=x*10+(c^48);c=getchar();}
return y?x:-x;
}
struct ed{
int u,v,w,net;
}edge[M<<1];
int head[M],num;
void addedge(int u,int v,int w)
{
edge[++num].u=u;
edge[num].v=v;
edge[num].w=w;
edge[num].net=head[u];
head[u]=num;
}
int f[M],sum[M];
void dfs_1(int u,int fa)
{
f[u]=fa;
for(int i=head[u];i;i=edge[i].net)
{
int v=edge[i].v;
if(v==fa) continue;
dfs_1(v,u);
}
}
int dis,fg;
bool dfs(int u,int fa,int t,int W)
{
for(int i=head[u];i;i=edge[i].net)
{
int v=edge[i].v;
if(v==fa) continue;//later fix it
dis+=edge[i].w;
if(v==t && dis==W) {fg=1;return true;}
else if(v==t) return false;
else
{
dfs(v,u,t,W),dis-=edge[i].w;
if(fg==1) return true;
return false;
}
}
}
int main()
{
freopen("lab.in","r",stdin);
freopen("lab.out","w",stdout);
n=read(),q=read();
for(int i=1,u,v,w;i<n;i++)
{
u=read(),v=read(),w=read();
addedge(u,v,w);
addedge(v,u,-w);
}
dfs_1(1,0);
while(q--)
{
int op=read(),u=read(),v=read(),w=read();
if(op==0) addedge(u,v,w),addedge(v,u,-w);
if(op==1) if(dfs(u,f[u],v,w)) printf("yes\n");else printf("no\n");
}
return 0;
}
/*
5 3
1 2 1
2 3 1
3 4 1
4 5 2
1 1 5 5
1 2 5 5
1 1 5 10
*/
civilization
Description
\(R\) 君在继续着宇宙社会学的研究,\(R\) 君发现是否为善意的文明与他们的距离到本文明的距离的奇偶有很大的关系。
所以 \(R\) 君提出了如下简化的问题,考虑 \(n\) 个节点带边权的树,两点间距离是两点间树上路径的边权和。
\(R\) 君想知道对于一个点来说,到这个点是距离奇数的节点的距离和,与到这个点距离是偶数的节点的距离和。
Solution
直接暴力骗分,然后我的读入读出文件写成了
freopen("civilization","r",stdin);
成功暴毙。
换根 dp
初始:
设 \(fu,0/1\) , \(fu,0/1\) 表示以 \(u\) 为根的子树中 到 \(u\) 点距离为 偶数/奇数 的点的距离和,转移考虑 \(w_{u,v}\) 的奇偶性 同时需要维护到 \(u\) 点距离为 偶数/奇数 的点的数量
换根:
\(fu,v\) 表示到 \(u\) 点距离为 偶数/奇数 的点的距离和
同样转移考虑 \(w_{u,v}\) 的奇偶性此时仍然需要动态的维护全树中到当前根节点距离为 偶数/奇数 的点的个数。(复制的BS的,代码看的吴清月dalao的%%%)
Code
/*
* @Author: smyslenny
* @Date: 2021.08.
* @Title:
* @Main idea:
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <cstring>
#include <cstdlib>
#include <queue>
#include <vector>
#define ll long long
#define INF 0x3f3f3f3f
#define orz cout<<"LKP AK IOI\n"
#define MAX(a,b) (a)>(b)?(a):(b)
#define MIN(a,b) (a)>(b)?(a):(b)
using namespace std;
const int mod=998244353;
int n,q;
int read()
{
int x=0,y=1;
char c=getchar();
while(c<'0' || c>'9') {if(c=='-') y=0;c=getchar();}
while(c>='0' && c<='9') { x=x*10+(c^48);c=getchar();}
return y?x:-x;
}
namespace substack1{
const int M=1e3+5;
int dis[M][M];
void main()
{
memset(dis,63,sizeof(dis));
for(int i=1;i<=n;i++) dis[i][i]=0;
for(int i=1,u,v,w;i<n;i++)
{
u=read(),v=read(),w=read();
dis[u][v]=w;
dis[v][u]=w;
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
while(q--)
{
int x=read();
int js=0,os=0;
for(int i=1;i<=n;i++)
{
if(i==x) continue;
dis[i][x]%2?js+=dis[i][x]:os+=dis[i][x];
}
printf("%d %d\n",js,os);
}
}
}
namespace substack2{
const int M=1e5+5;
struct ed{
int u,v,w,net;
}edge[M<<1];
int head[M],num;
void addedge(int u,int v,int w)
{
edge[++num].u=u;
edge[num].v=v;
edge[num].w=w;
edge[num].net=head[u];
head[u]=num;
}
int num_1[M],num_2[M],dis[M],dis_1[M],dis_2[M],num_1_re[M],num_2_re[M],dis_1_re[M],dis_2_re[M],f[M];
void dfs1(int u)
{
num_1[u]++;
for(int i=head[u];i;i=edge[i].net)
{
int v=edge[i].v;
if(v==f[u])continue;
f[v]=u;
dis[v]=edge[i].w;
dfs1(v);
if(dis[v]&1)
{
dis_1[u]+=dis_2[v]+num_2[v]*dis[v];
dis_2[u]+=dis_1[v]+num_1[v]*dis[v];
num_1[u]+=num_2[v];
num_2[u]+=num_1[v];
}
else
{
dis_1[u]+=dis_1[v]+num_1[v]*dis[v];
dis_2[u]+=dis_2[v]+num_2[v]*dis[v];
num_1[u]+=num_1[v];
num_2[u]+=num_2[v];
}
}
}
void dfs2(int u)
{
if(dis[u]&1)
{
dis_1_re[u]+=dis_2_re[f[u]]+num_2_re[f[u]]*dis[u];
dis_1_re[u]+=dis_2[f[u]]-dis_1[u]-num_1[u]*dis[u]+(num_2[f[u]]-num_1[u])*dis[u];
dis_2_re[u]+=dis_1_re[f[u]]+num_1_re[f[u]]*dis[u];
dis_2_re[u]+=dis_1[f[u]]-dis_2[u]-num_2[u]*dis[u]+(num_1[f[u]]-num_2[u])*dis[u];
num_1_re[u]+=num_2_re[f[u]]+num_2[f[u]]-num_1[u];
num_2_re[u]+=num_1_re[f[u]]+num_1[f[u]]-num_2[u];
}
else
{
dis_1_re[u]+=dis_1_re[f[u]]+num_1_re[f[u]]*dis[u];
dis_1_re[u]+=dis_1[f[u]]-dis_1[u]-num_1[u]*dis[u]+(num_1[f[u]]-num_1[u])*dis[u];
dis_2_re[u]+=dis_2_re[f[u]]+num_2_re[f[u]]*dis[u];
dis_2_re[u]+=dis_2[f[u]]-dis_2[u]-num_2[u]*dis[u]+(num_2[f[u]]-num_2[u])*dis[u];
num_1_re[u]+=num_1_re[f[u]]+num_1[f[u]]-num_1[u];
num_2_re[u]+=num_2_re[f[u]]+num_2[f[u]]-num_2[u];
}
for(int i=head[u];i;i=edge[i].net)
{
int v=edge[i].v;
if(v==f[u])continue;
dfs2(v);
}
}
void main()
{
for(int i=1,u,v,w;i<n;i++)
u=read(),v=read(),w=read(),addedge(u,v,w),addedge(v,u,w);
dfs1(1);
dis_1[0]=dis_1[1];dis_2[0]=dis_2[1];num_1[0]=num_1[1];num_2[0]=num_2[1];
dfs2(1);
for(int i=1,x;i<=q;i++)
x=read(),printf("%d %d\n", dis_2[x]+dis_2_re[x],dis_1[x]+dis_1_re[x]);
}
}
int main()
{
// freopen("civilization.in","r",stdin);
// freopen("civilization.out","w",stdout);
n=read(),q=read();
if(n<=100) substack1::main();
else
substack2::main();
return 0;
}
/*
4 4
1 2 1
2 3 2
2 4 3
1
2
3
4
*/