模拟58 考试总结
不要停下来啊
考试经过
一言难尽。。。
把T1无向图看成有向图,然后直接弃了,T250分没有,原因是没注意不是质数没有逆元
T3没看出来AC自动机写的暴力,T4倒是胡了个网络流暴力,然而数组开小少了10分
现在犯过的错误以后一定不能再犯了,尤其是这个质数
T2.贝尔数
别问为啥没T1
模数非质数,所以暴力的话不能用费马也不能线性,只能杨辉三角递推
正解是用CRT把大数拆成5个质数连乘,每个用矩阵快速幂算就行
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int modd=95041567;
int m[7]={0,31,37,41,43,47};
int f[6][55][55],bell[6][55];
inline int ksm(int x,int y,int mod)
{
int s=1;x%=mod;
for(;y;y>>=1)
{
if(y&1)s=s*x%mod;
x=x*x%mod;
}
return s;
}
inline int ny(int x,int mod){return ksm(x,mod-2,mod);}
int a[50],b[50],p[50][50],pp[50][50];
inline void getp(int mod)
{
memset(p,0,sizeof(p));
for(int i=2;i<=mod;i++)p[i][i-1]=1;
p[1][mod]=p[2][mod]=1;
}
inline void mul(int mod)
{
memset(pp,0,sizeof(pp));
for(int i=1;i<=mod;i++)
for(int j=1;j<=mod;j++)
for(int k=1;k<=mod;k++)
pp[i][j]=(pp[i][j]+p[i][k]*p[k][j]%mod)%mod;
memcpy(p,pp,sizeof(p));
}
inline void gan(int mod)
{
memset(b,0,sizeof(b));
for(int i=1;i<=mod;i++)
for(int j=1;j<=mod;j++)
b[i]=(b[i]+a[j]*p[j][i]%mod)%mod;
memcpy(a,b,sizeof(b));
}
inline void ga(int x,int mod)
{
for(;x;x>>=1)
{
if(x&1)gan(mod);
mul(mod);
}
}
signed main()
{
freopen("bell.in","r",stdin);
freopen("bell.out","w",stdout);
for(int x=1;x<=5;x++)
{
int mod=m[x];
f[x][0][0]=1;
for(int i=1;i<=50;i++)
{
f[x][i][0]=1;
for(int j=1;j<i;j++)f[x][i][j]=(f[x][i-1][j-1]+f[x][i-1][j])%mod;
f[x][i][i]=1;
}
bell[x][0]=1;
for(int i=1;i<=mod;i++)
for(int j=0;j<i;j++)
bell[x][i]=(bell[x][i]+f[x][i-1][j]*bell[x][j]%mod)%mod;
}
int t;cin>>t;
while(t--)
{
int n;scanf("%lld",&n);
int ans=0;
for(int x=1;x<=5;x++)
{
int mod=m[x],mm=modd/mod;
getp(mod);memset(a,0,sizeof(a));
for(int i=1;i<=mod;i++)a[i]=bell[x][i];
ga(n-1,mod);int an=a[1];
ans=(ans+an*mm%modd*ny(mm,mod)%modd)%modd;
}
printf("%lld\n",ans);
}
return 0;
}
T3. 穿越广场
AC自动机dp,考场没看出来
设状态\(f_{i,j,k,s}\)表示当前串长,其中一个字母数量,节点,匹配状态(二进制),随便转移就行
注意D和R要一一对应,包括节点和状态
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=205;
const int mod=1e9+7;
int tr[N][2],fail[2*N],tot=1;
char s[3][N];// D 0 R 1
int v[N*2];
inline void ins(int x)
{
int u=1,l=strlen(s[x]+1);
for(int i=1;i<=l;i++)
{
int op=(s[x][i]=='D')?0:1;
if(!tr[u][op])tr[u][op]=++tot;
u=tr[u][op];
}
v[u]|=(1<<(x-1));
}
queue <int> q;
inline void build()
{
for(int i=0;i<=1;i++)
{
if(tr[1][i])q.push(tr[1][i]),fail[tr[1][i]]=1;
else tr[1][i]=1;
}
while(q.size())
{
int u=q.front();q.pop();
if(v[fail[u]])v[u]|=v[fail[u]];
for(int i=0;i<=1;i++)
{
if(tr[u][i])q.push(tr[u][i]),fail[tr[u][i]]=tr[fail[u]][i];
else tr[u][i]=tr[fail[u]][i];
}
}
}
int f[N][105][N][4];
inline void clear()
{
memset(tr,0,sizeof(tr));tot=1;
memset(fail,0,sizeof(fail));
memset(v,0,sizeof(v));
memset(f,0,sizeof(f));
}
signed main()
{
freopen("square.in","r",stdin);
freopen("square.out","w",stdout);
int t;cin>>t;
while(t--)
{
clear();
int m,n;scanf("%lld%lld",&m,&n);
scanf("%s%s",s[1]+1,s[2]+1);
ins(1);ins(2);build();
f[0][0][1][0]=1;
for(int i=0;i<=n+m;i++)
for(int j=0;j<=n;j++)
for(int k=1;k<=tot;k++)
for(int s=0;s<=3;s++)
{
f[i+1][j+1][tr[k][0]][s|v[tr[k][0]]]=(f[i+1][j+1][tr[k][0]][s|v[tr[k][0]]]+f[i][j][k][s])%mod;
f[i+1][j][tr[k][1]][s|v[tr[k][1]]]=(f[i+1][j][tr[k][1]][s|v[tr[k][1]]]+f[i][j][k][s])%mod;
}
int ans=0;
for(int i=1;i<=tot;i++)ans=(ans+f[n+m][n][i][3])%mod;
printf("%lld\n",ans);
}
return 0;
}
T4.舞动的夜晚
蓝书上原题,二分图匹配可行边
证明看蓝书吧,结论也有,全是板子冲就完了
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=20050,M=200050;
inline int read()
{
int x=0;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return x;
}
struct node1{
int from,to,next,w;
}a[2*M];
int head[N],mm=2;
inline void add(int x,int y,int w)
{
a[mm].from=x;a[mm].to=y;a[mm].w=w;
a[mm].next=head[x];head[x]=mm++;
}
inline void gan(int x,int y,int w)
{
add(x,y,w);add(y,x,0);
}
int s,t,n,m,p,tot;
int d[N],hd[N];
queue <int> q;
inline bool bfs()
{
memset(d,0x3f,sizeof(d));
memcpy(head,hd,sizeof(head));
while(q.size())q.pop();
q.push(s);d[s]=0;
while(q.size())
{
int x=q.front();q.pop();
for(int i=head[x];i;i=a[i].next)
{
int y=a[i].to;
if(!a[i].w)continue;
if(d[y]>d[x]+1)d[y]=d[x]+1,q.push(y);
}
if(x==t)return 1;
}
return 0;
}
int dfs(int x,int flow)
{
if(x==t)return flow;
int rest=flow,k;
for(int i=head[x];i;head[x]=i=a[i].next)
if(a[i].w)
{
int y=a[i].to;
if(d[y]==d[x]+1)
{
k=dfs(y,min(a[i].w,rest));
if(!k)d[y]=0;
else a[i].w-=k,a[i^1].w+=k,rest-=k;
}
if(!rest)break;
}
return flow-rest;
}
int mp1[N],mp2[N],r[M];
struct node2{
int from,to,next;
}b[2*M];
inline void add2(int x,int y)
{
b[mm].from=x;b[mm].to=y;
b[mm].next=head[x];head[x]=mm++;
}
int dfn[N],low[N],ga[N],num,idd;
vector <int> an;
stack <int> st;bool v[N];
void tarjan(int x)
{
dfn[x]=low[x]=++num;
st.push(x);v[x]=1;
for(int i=head[x];i;i=b[i].next)
{
int y=b[i].to;
if(!dfn[y])
{
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(v[y])low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x])
{
int top;idd++;
do{
top=st.top();v[top]=0;
ga[top]=idd;st.pop();
}while(x!=top);
}
}
signed main()
{
freopen("night.in","r",stdin);
freopen("night.out","w",stdout);
cin>>n>>m>>p;
s=++tot;t=++tot;
for(int i=1;i<=n;i++)mp1[i]=++tot,gan(s,mp1[i],1);
for(int i=1;i<=m;i++)mp2[i]=++tot,gan(mp2[i],t,1);
for(int i=1;i<=p;i++)
{
int x=read(),y=read();
r[i]=mm;
gan(mp1[x],mp2[y],1);
}
memcpy(hd,head,sizeof(head));
while(bfs())dfs(s,1e9);
mm=1;int sum=0;memset(head,0,sizeof(head));
for(int i=1;i<=p;i++)
{
int id=r[i],x=a[id].from,y=a[id].to;
if(a[id].w)add2(x,y);
else add2(y,x);
}
for(int i=hd[s];i;i=a[i].next)
{
int y=a[i].to;
if(a[i].w)add2(s,y);
else add2(y,s);
}
for(int i=hd[t];i;i=a[i].next)
{
int y=a[i].to;
if(a[i^1].w)add2(y,t);
else add2(t,y);
}
for(int i=1;i<=tot;i++)if(!dfn[i])tarjan(i);
for(int i=1;i<=p;i++)
{
int id=r[i],x=a[id].from,y=a[id].to;
if(!a[id].w)continue;
if(ga[x]==ga[y])continue;
sum++;an.push_back(i);
}
cout<<sum<<endl;
for(int i=0;i<an.size();i++)printf("%lld ",an[i]);
return 0;
}
T1.Lesson5!
因为这个本来改A了,结果luogu上被hack了,调了一上午。。。
有向图显然可以拓扑排序,正反做一遍,那么一条路径可以表示为他起点正拓扑序+终点逆拓扑序+1
由于每一条边都可以表示一条路径,所以可以方便解决删点问题
按正拓扑序枚举点,实现一个数据结构存最长路,扫到这个点时:
删掉入边——统计答案——加入出边
一个多重集完全可以实现,可删堆或者权值线段树也是可以的
强烈建议建出源汇点,这样省很多细节
原理的话是拓扑序保证了不重不漏,所有边也都统计过,反正大佬的题解都挺清楚的。。。
放一个某谷上AC代码,没加文件和多测
#include <bits/stdc++.h>
using namespace std;
const int N=500050,M=1000050;
inline int read()
{
int x=0;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return x;
}
struct node{
int from,to,next,op;
}a[2*M];
int head[N],mm=1;
inline void add(int x,int y,int op)
{
a[mm].from=x;a[mm].to=y;a[mm].op=op;
a[mm].next=head[x];head[x]=mm++;
}
int n,m,du1[N],du2[N];
int p1[N],p2[N];
vector <int> num[N];
multiset <int> s;
queue <int> q;
inline void de(int x)
{
auto it=s.find(x);
if(it!=s.end())s.erase(it);
}
inline int get()
{
if(s.size())return *s.rbegin();
else return (int)1e9;
}
signed main()
{
n=read(),m=read();
for(int i=1;i<=m;i++)
{
int x=read(),y=read();
add(x,y,1);add(y,x,0);
du1[y]++;du2[x]++;
}
int ss=n+1,tt=n+2;
for(int i=1;i<=n;i++)
{
add(ss,i,1);add(i,ss,0);
add(i,tt,1);add(tt,i,0);
du1[i]++;du2[ss]++;
du1[tt]++;du2[i]++;
}
q.push(ss);
while(q.size())
{
int x=q.front();q.pop();
for(int i=head[x];i;i=a[i].next)
{
if(a[i].op==0)continue;
int y=a[i].to;
if(p1[y]<p1[x]+1)p1[y]=p1[x]+1;
du1[y]--;if(!du1[y])q.push(y);
}
}
q.push(tt);
while(q.size())
{
int x=q.front();q.pop();
for(int i=head[x];i;i=a[i].next)
{
if(a[i].op==1)continue;
int y=a[i].to;
if(p2[y]<p2[x]+1)p2[y]=p2[x]+1;
du2[y]--;if(!du2[y])q.push(y);
}
}
for(int i=1;i<=n+2;i++)num[p1[i]].push_back(i);
int ans=1e9,anss=1e9;
for(int i=0;i<=n+2;i++)
{
if(!num[i].size())continue;
for(int j=0;j<num[i].size();j++)
{
int x=num[i][j];
for(int ii=head[x];ii;ii=a[ii].next)
{
int y=a[ii].to;
if(a[ii].op==1)continue;
int w=p1[y]+p2[x]-1;
de(w);
}
int an=get();
if(an<ans)ans=an,anss=x;
else if(an==ans)anss=min(anss,x);
for(int ii=head[x];ii;ii=a[ii].next)
{
int y=a[ii].to;
if(a[ii].op==0)continue;
int w=p1[x]+p2[y]-1;
s.insert(w);
}
}
}
printf("%d %d\n",anss,ans);
return 0;
}
考试总结
细节还是细节,任何一个细小的地方都会引起大的失误或挂分
多打表,多对拍,多动键盘,不要干坐着
予明日所有失败者 赋万千不灭颂歌

浙公网安备 33010602011771号