@happyforever
2018-10-29T14:27:16.000000Z
字数 8528
阅读 520
解题报告 清北学堂

期望得分:
实际得分:
写了半小时的KMP
调了一个半小时的AC自动机
发现不会改就放过了
hash有想过但是实在怕被卡就没有写
然后号上午写了一份unsigned long long自然溢出的代码过了。。。
因为输入数据比较大,有的人没写快读就很亏,然后老师开了的时限
。。。。
开我也过不了。。。
小数据范围没写,只写了特殊性质,然后了。
数据锅了hhhh
部分分的数据范围与题目给的范围不一样
分Floyd
分每次暴搜

/** 有人说是AC自动机。。* 吔* 我只会KMP是不是凉了2333* O(Ln),最坏情况O(1e9)*/#include <cstdio>#include <cstring>const int N=1e5+1;int L,n,m,next[21];char s[N],ss[N/10+1][21];bool flag=true;void getNext(char *ss){for(int j=0,i=1;i<m;++i){while(j && ss[i]!=ss[j])j=next[j];if(ss[i]==ss[j])next[i+1]=++j;else next[i+1]=0;}}void KMP(char *ss){for(int j=0,i=0;i<L;++i){while(j && s[i]!=ss[j])j=next[j];if(s[i]==ss[j])++j;if(j==m){flag=false;printf("%d\n",i-m+2);return ;}}}int main(){freopen("monitor.in","r",stdin);freopen("monitor.out","w",stdout);scanf("%d%d%d",&L,&n,&m);for(int i=1;i<=n;++i)scanf("%s",ss[i]);scanf("%s",s);for(int i=1;i<=n;++i){getNext(ss[i]);KMP(ss[i]);if(!flag)goto E;}if(flag)puts("no");E: fclose(stdin),fclose(stdout);return 0;}

/** 多加了边就会形成环* 不存在k=0考虑LCA求解* 有一个环的话*/#include <cstdio>#include <iostream>inline int read(){int n=0,w=1;register char c=getchar();while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();return n*w;}const int N=1e5+1;int n,q,tot,head[N];int val[N],deep[N],fa[N],son[N],size[N],top[N],idx[N];struct Edge{int v,w,nxt;}edge[N];inline void add(int w,int v,int u){edge[++tot]=(Edge){v, w,head[u]};head[u]=tot;edge[++tot]=(Edge){u,-w,head[v]};head[v]=tot;}void dfs1(int now){size[now]=1;for(int v,i=head[now];i;i=edge[i].nxt)if((v=edge[i].v)!=fa[now]){deep[v]=deep[now]+1;val[v]=edge[i].w;fa[v]=now;dfs1(v);size[now]+=size[v];if(size[son[now]]<size[v])son[now]=v;}}void dfs2(int now,int tp){top[now]=tp;if(!son[now])return ;dfs2(son[now],tp);for(int i=head[now];i;i=edge[i].nxt)if(edge[i].v!=fa[now])dfs2(edge[i].v,edge[i].v);}inline int Lca(int x,int y){while(top[x]!=top[y]){if(deep[top[x]]<deep[top[y]])std::swap(x,y);x=fa[top[x]];}return deep[x]>deep[y]?y:x;}inline int query(int x,int y){int lca=Lca(x,y);long long ans=0;while(x!=lca){ans-=val[x];x=fa[x];}while(y!=lca){ans+=val[y];y=fa[y];}// printf("%d",ans);return ans;}int main(){freopen("lab.in","r",stdin);freopen("lab.out","w",stdout);n=read(),q=read();for(int i=1;i<n;++i)add(read(),read(),read());dfs1(1);dfs2(1,1);int k,x,y,z;while(q--){k=read(),x=read(),y=read(),z=read();puts(query(x,y)==z?"yes":"no");}fclose(stdin),fclose(stdout);return 0;}

/** 40分可以floyd* 考虑对每个点,维护其到其子树内各点的奇、偶路径长度和*/#include <cstdio>#include <cstring>const int N=1e5+1;int n,q,tot,head[N];struct Edge{int v,w,nxt;}edge[N<<1];long long ans[2];inline void add(int w,int v,int u){edge[++tot]=(Edge){v,w,head[u]};head[u]=tot;edge[++tot]=(Edge){u,w,head[v]};head[v]=tot;}inline int read(){int n=0,w=1;register char c=getchar();while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();return n*w;}void dfs(int now,int fa,long long w){ans[w&1]+=w;for(int v,i=head[now];i;i=edge[i].nxt)if((v=edge[i].v)!=fa)dfs(v,now,edge[i].w+w);}int main(){freopen("civilization.in","r",stdin);freopen("civilization.out","w",stdout);n=read(),q=read();for(int i=1;i<n;++i)add(read(),read(),read());int x;while(q--){x=read();ans[1]=ans[0]=0;dfs(x,x,0);printf("%I64d %I64d\n",ans[1],ans[0]);}fclose(stdin),fclose(stdout);return 0;}
遍KMP,最坏复杂度可以得
用std::string里面的find函数,因为每次find是最坏复杂度的,所以最坏,也能分
hash。。。某同学:“老师,会卡hash吗”。老师:“我有一晚上的时间”。
没有卡,都卡掉了
其他比较随意的也没有卡
std::map因为时间问题会一个点
#include <set>#include <cstdio>#include <cstring>#include <algorithm>int L,n,m;typedef unsigned long long LLU;const LLU mod=1000000000+7;const LLU mod2=1000000000+9;const LLU base = 131;LLU h[10005];LLU h2[10005];char s[100005];LLU gethash(char *s){LLU ret = 0;for(int i=0;s[i];++i)ret=(ret*base+s[i])%mod;return ret;}LLU gethash2(char *s){LLU ret=0;for(int i=0;s[i];++i)ret=(ret*base+s[i]) % mod2;return ret;}std::set<std::pair<LLU,LLU> > mingan;int main(){freopen("monitor.in", "r", stdin);freopen("monitor.out", "w", stdout);scanf("%d%d%d",&L,&n,&m);for(int i=1;i<=n;++i){scanf("%s",s+1);h[i]=gethash(s+1);h2[i]=gethash2(s+1);mingan.insert(make_pair(h[i],h2[i]));}scanf("%s",s+1);LLU now=0,now2=0,basem=1,basem2=1;bool flag=0;for(int i=1;i<=m;++i)basem=basem*base%mod,basem2=basem2*base%mod2;for(int i=1;i<=L;++i){now=(now*base+s[i])%mod;now2=(now2*base+s[i])%mod2;if(i>m){now=((now-s[i-m]*basem%mod)+mod)%mod;now2=((now2-s[i-m]*basem2%mod2)+mod2)%mod2;}if(mingan.count(make_pair(now,now2))!=0){printf("%d\n",i-m+1);flag=1;break;}}if(!flag)puts("no");return 0;}
当没有的操作时,就是在树上求两点之间的路径长度是否等于某个值
通常的想法是
换个想法,假设现在有一棵树如下

现在求的路径长度
设表示从点到根节点的距离,那么就是蓝色线,是紫色线

再考虑所代表的含义
即
橙色的线段代表的路径,从指向
也就是到的路径长度
由此,对于的数据,可以预处理,而后每次输出答案
对于,即只有一个环的数据,考虑环对答案的贡献

考虑如上的一张图,询问到是否有某长度的路径,的权值与是相反数
所以从走到再走回去只会增加(或减少)环的大小的时间
虽然没有给有两个(有两个环)的情况,但我们应该用它来考虑向正解的靠近
不妨先考虑两个环互不影响的情况

假设第一个环的大小是,第二个环的大小是,
那么就是要求同余方程是否有解
根据裴蜀定理,同余方程有解当且仅当
所以我们要求的就是上述方程是否有解
再考虑更一般的情况:可能有很多环
那么先求一遍每个点到根的路径,再在操作过程中对所有产生的环求其大小(假定有个环,大小分别为),询问时,一个数不能被拼出来当且仅当
#include <cmath>#include <cstdio>#include <cstring>const int N=100001;int n,q,head[N],tot;struct Edge{int v,w,nxt;}edge[N<<1];inline void add(int w,int v,int u){edge[++tot]=(Edge){v, w,head[u]};head[u]=tot;edge[++tot]=(Edge){u,-w,head[v]};head[v]=tot;}inline int read(){int n=0,w=1;register char c=getchar();while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();return n*w;}typedef long long LL;LL sum[100005];void dfs(int x,int fa){for(int v,i=head[x];i;i=edge[i].nxt)if((v=edge[i].v)!=fa){sum[edge[i].v]=sum[x]+edge[i].w;dfs(v,x);}}LL gcd(LL a, LL b){return b==0?a:gcd(b,a%b);}int main(){freopen("lab.in","r",stdin);freopen("lab.out","w",stdout);n=read(),q=read();for(int i=1;i<n;++i)add(read(),read(),read());dfs(1,0);LL g=0;for(int i=0,k,x,y,t;i<q;++i){k=read(),x=read(),y=read(),t=read();if(k==0){if(!g)g=abs(sum[x]-sum[y]+t);elseif(abs(sum[x]-sum[y]+t)%g!=0)g=gcd(g,abs(sum[x]-sum[y]+t));}elseif(k==1){if(sum[y]-sum[x]==t)//不需要环puts("yes");elseif(g==0)//没有环puts("no");elseif((t-(sum[y]-sum[x]))%g==0)puts("yes");else puts("no");}}fclose(stdin);fclose(stdout);return 0;}
考虑求一个点的子树中所有点到这个点的权值和
void dfs1(int x,int fa){size[x]=1;for(int v,i=head[x];i;i=edge[i].nxt)if((v=edge[i].v)!=fa){dfs1(v,x);size[x]+=size[v];f[x]+=f[v]+edge[i].w*size[v];}}
那么如果能够从父亲节点推到儿子节点就可以了
对每个点,把当前点的子树的贡献刨掉
f2[1]=f[1];void dfs2(int x){for(int v,i=head[x];i;i=edge[i].nxt)if((v=edge[i].v)!=fa){f2[v] =f[v];f2[v]+=f2[x]-f[v]-edge[i].w*size[v]+edge[i].w*(size[1]-size[x]);dfs2(v,x);}}
另外注意在经过一条奇边之后,奇数路径和与偶数路径和要对换;经过一条偶边之后奇数路径和与偶数路径和仅仅数值变化不会对换
#include <cstdio>#include <cstring>#include <algorithm>// 70%给开了int没开longlongconst int N=100001;int n,q,head[N],tot;struct Edge{int v,w,nxt;}edge[N<<1];inline void add(int w,int v,int u){edge[++tot]=(Edge){v, w,head[u]};head[u]=tot;edge[++tot]=(Edge){u,-w,head[v]};head[v]=tot;}inline int read(){int n=0,w=1;register char c=getchar();while(c<'0' || c>'9'){if(c=='-')w=-1;c=getchar();}while(c>='0' && c<='9')n=n*10+c-'0',c=getchar();return n*w;}typedef long long LL;LL f[2][N];LL ans[2][N];int siz[2][N];int siz2[2][N];void dfs1(int x,int fa){siz[0][x]=1;for(int v,i=head[x];i;i=edge[i].nxt){if((v=edge[i].v)==fa) continue;dfs1(v,x);if(edge[i].w&1){siz[0][x]+=siz[1][v];siz[1][x]+=siz[0][v];f[0][x]+=f[1][v]+edge[i].w*siz[1][v];f[1][x]+=f[0][v]+edge[i].w*siz[0][v];}else{siz[0][x]+=siz[0][v];siz[1][x]+=siz[1][v];f[0][x]+=f[0][v]+edge[i].w*siz[0][v];f[1][x]+=f[1][v]+edge[i].w*siz[1][v];}}}void dfs2(int x,int fa){for(int v,i=head[x];i;i=edge[i].nxt){if((v=edge[i].v)==fa)continue;if(edge[i].w&1){siz2[0][v]=siz2[1][x];siz2[1][v]=siz2[0][x];ans[0][v]=f[0][v]+ans[1][x]-(f[0][v]+edge[i].w*siz[0][v])+edge[i].w*(siz2[1][x]-siz[0][v]);ans[1][v]=f[1][v]+ans[0][x]-(f[1][v]+edge[i].w*siz[1][v])+edge[i].w*(siz2[0][x]-siz[1][v]);}else{siz2[0][v]=siz2[0][x];siz2[1][v]=siz2[1][x];ans[0][v]=f[0][v]+ans[0][x]-(f[0][v]+edge[i].w*siz[0][v])+edge[i].w*(siz2[0][x]-siz[0][v]);ans[1][v]=f[1][v]+ans[1][x]-(f[1][v]+edge[i].w*siz[1][v])+edge[i].w*(siz2[1][x]-siz[1][v]);}dfs2(v,x);}}int main(){freopen("civilization.in","r",stdin);freopen("civilization.out","w",stdout);n=read(),q=read();for(int i=1;i<n;++i)add(read(),read(),read());dfs1(1,0);ans[0][1]=f[0][1];ans[1][1]=f[1][1];siz2[0][1]=siz[0][1];siz2[1][1]=siz[1][1];dfs2(1,0);int x;while(q--){x=read();printf("%lld %lld\n",ans[1][x],ans[0][x]);// printf("f %lld %lld\n",f[1][x],f[0][x]);// printf("siz %d %d\n",siz[1][x],siz[0][x]);}fclose(stdin);fclose(stdout);return 0;}