noip模拟58[我拉了]
noip模拟58 solutions
主要是我心情不好,所以导致我考的非常完蛋。
做了两个题之后发现啥也不会,所以我就不想做了。
所以我只有70pts,惨死了,改题也不是很顺利。。。。
T1 lesson5
气死我了呜呜呜呜
考试上来就觉得这个题非常的操蛋
\(TMD\)是有向图,我上来就按着无向图手摸样例,然后我就直接弃掉这个题了
所以,一定要看题,仔细看,一个字也不能落下
有向图的话,直接正反分别拓扑一遍
得到从起点的最长路,到终点的最长路,这个暴力的话就可以直接枚举最长路上的点一个一个删
不过这个正解就直接把线段树干上去了
首先我们知道对于每一条边,他的贡献都是\(dis_u+dis_v+1\)于是我们可以在线段树上维护这个东西
但是你发现维护的时候,你无法将这条边的所有贡献都删掉,比如说对后面的贡献
这个时候拓扑序就有了极其重要的作用
我们先把反着走的\(dis\)插入到线段树上,这样我们就有了最大的距离,因为一定有一个点是最大距离的起点
我们按照正着走的拓扑序枚举所有的点,
每到达一个点,我们就将它反着走的\(dis\)在线段树上删除,插入正着走的\(dis\)
并且我们还要将它所有的连边,也按照上面的,插入正着的,删除反着的
这样我们在保证最长路存在的情况下,使你当前删除的点的影响被消除
AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=1e5+5;
const int M=5e5+5;
int T,n,m;
struct EDGE{
int to[M],nxt[M],head[N],rp;
int rd[N],xu[N],dis[N];
void add_edg(int x,int y){
to[++rp]=y;rd[y]++;
nxt[rp]=head[x];
head[x]=rp;
}
queue<int> q;
void topu(){
fo(i,1,n)if(!rd[i])xu[++xu[0]]=i,q.push(i);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=head[x];i;i=nxt[i]){
int y=to[i];dis[y]=max(dis[y],dis[x]+1);rd[y]--;
if(!rd[y])xu[++xu[0]]=y,q.push(y);
}
}
}
void init(){
memset(head,0,sizeof(head));rp=0;
memset(rd,0,sizeof(rd));xu[0]=0;
memset(dis,0,sizeof(dis));
}
}one,two;
struct XDS{
#define ls x<<1
#define rs x<<1|1
int sum[M*4],siz[M*4];
void pushup(int x){
sum[x]=max(sum[ls],sum[rs]);
return ;
}
void ins(int x,int l,int r,int pos,int v){
if(l==r){
siz[x]+=v;
if(siz[x]>0)sum[x]=l;
else sum[x]=-1;
return ;
}
int mid=l+r>>1;
if(pos<=mid)ins(ls,l,mid,pos,v);
else ins(rs,mid+1,r,pos,v);
pushup(x);return ;
}
void init(){
memset(sum,0,sizeof(sum));
memset(siz,0,sizeof(siz));
}
#undef ls
#undef rs
}xds;
int ans1,ans2;
signed main(){
#ifdef oj
freopen("johnny.in","r",stdin);
freopen("johnny.out","w",stdout);
#endif
scanf("%d",&T);
while(T--){
xds.init();
one.init();
two.init();
ans1=1e9;ans2=1e9;
scanf("%d%d",&n,&m);
fo(i,1,m){
int x,y;scanf("%d%d",&x,&y);
one.add_edg(x,y);two.add_edg(y,x);
}
one.topu();two.topu();
fo(i,1,n)xds.ins(1,0,n,two.dis[i],1);
fo(x,1,n){
int now=one.xu[x];
for(int i=two.head[now];i;i=two.nxt[i]){
int y=two.to[i];
xds.ins(1,0,n,two.dis[now]+one.dis[y]+1,-1);
}
xds.ins(1,0,n,two.dis[now],-1);
if(xds.sum[1]<ans2||(xds.sum[1]==ans2&&now<ans1))ans1=now,ans2=xds.sum[1];
for(int i=one.head[now];i;i=one.nxt[i]){
int y=one.to[i];
xds.ins(1,0,n,one.dis[now]+two.dis[y]+1,1);
}
xds.ins(1,0,n,one.dis[now],1);
}
printf("%d %d\n",ans1,ans2);
}
}
T2 贝尔数
就这个其实对于前50pts,这就是送分的
但是后面的就有点难了
你发现这个模数根本就不是一个质数
并且你后面的那个模数转移式完全可以用矩阵快速幂优化
所以你对于每一个质数得到一个同余方程
最后用中国剩余定理合并一下就好了
AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=55;
const int mod=95041567;
int T,n,c[N][N],bel[N],p;
int ksm(int x,int y){
int ret=1;
while(y){
if(y&1)ret=ret*x%mod;
x=x*x%mod;y>>=1;
}return ret;
}
int sum[10]={0,31,37,41,43,47};
int ans[10];
struct matrix{
int a[N][N];
matrix(){memset(a,0,sizeof(a));}
matrix operator * (matrix x)const{
matrix ret;
fo(i,0,50)fo(j,0,50)fo(k,0,50)ret.a[i][j]=(ret.a[i][j]+a[i][k]*x.a[k][j])%p;
return ret;
}
}xs,ls;
matrix ksm(matrix x,int y){
matrix ret;
fo(i,0,50)ret.a[i][i]=1;
while(y){
if(y&1)ret=ret*x;
x=x*x;y>>=1;
}return ret;
}
int exgcd(int a,int b,int &x,int &y){
if(!b)return x=1,y=0,a;
int ret=exgcd(b,a%b,x,y);
int tmp=x;x=y;y=tmp-y*(a/b);
return ret;
}
signed main(){
#ifdef oj
freopen("bell.in","r",stdin);
freopen("bell.out","w",stdout);
#endif
fo(i,0,50){
c[i][0]=c[i][i]=1;
fo(j,1,i-1)c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
}
bel[0]=1;fo(i,1,50)fo(j,0,i-1)bel[i]=(bel[i]+c[i-1][j]*bel[j])%mod;
fo(i,1,50)xs.a[i-1][i]=xs.a[i][i]=1;
scanf("%lld",&T);
while(T--){
scanf("%lld",&n);int res=0;
if(n<=50){printf("%lld\n",bel[n]);continue;}
fo(i,1,5){
if(i>1)xs.a[sum[i-1]-1][0]=0;xs.a[sum[i]-1][0]=1;
fo(j,0,50)ls.a[0][j]=bel[j]%sum[i];
//fo(j,0,50){fo(k,0,50)cout<<xs.a[j][k]<<" ";cout<<endl;}cout<<endl;
int tmp=n/(sum[i]-1),ys=n%(sum[i]-1);p=sum[i];
matrix now=ls*ksm(xs,tmp);
ans[i]=now.a[0][ys];
//cout<<ans[i]<<endl<<endl;
int m=mod/sum[i],x,y;
exgcd(m,sum[i],x,y);
x=(x%sum[i]+sum[i])%sum[i];
res=(res+ans[i]*m%mod*x%mod)%mod;
}
xs.a[sum[5]-1][0]=0;
printf("%lld\n",res);
}
}
T3 穿越广场
所以这就是我水过AC自动机专题的后果????
我是真的不会AC自动机了
现在有一点点思路了,主要是继承其fail节点的信息
一会再去刷两道题就都会了
这个就是对两个串建立自动机,节点信息就是保存当前的节点可以包含那些子串
就直接做就好了
AC_code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=205;
const int mod=1e9+7;
int yg[N],an,T,n,m,ans;
char a[N];
struct ACauto{
int tr[N][2],seg,fail[N];
int sta[N],dp[N][N/2][N][4];
void ins(int id){
int now=0;
fo(i,1,an){
if(!tr[now][yg[a[i]-'A']])tr[now][yg[a[i]-'A']]=++seg;
//cout<<tr[now][yg[a[i]-'A']]<<endl;
now=tr[now][yg[a[i]-'A']];
}
sta[now]=id;
}
void build(){
queue<int> q;while(!q.empty())q.pop();
fo(i,0,1)if(tr[0][i])q.push(tr[0][i]);
while(!q.empty()){
int x=q.front();q.pop();
//cout<<x<<endl;
fo(i,0,1){
if(tr[x][i]){
fail[tr[x][i]]=tr[fail[x]][i];
sta[tr[x][i]]|=sta[tr[fail[x]][i]];
q.push(tr[x][i]);
}
else tr[x][i]=tr[fail[x]][i];
}
}
}
void get_ans(){
dp[0][0][0][0]=1;
fo(i,0,n+m){
fo(j,0,n){
fo(k,0,seg){
fo(s,0,3){
int e1=tr[k][0],e2=tr[k][1];
(dp[i+1][j+1][e1][s|sta[e1]]+=dp[i][j][k][s])%=mod;
(dp[i+1][j][e2][s|sta[e2]]+=dp[i][j][k][s])%=mod;
}
}
}
}
fo(i,0,seg)ans=(ans+dp[n+m][n][i][3])%mod;
}
void clear(){
memset(tr,0,sizeof(tr));seg=0;
memset(dp,0,sizeof(dp));
memset(fail,0,sizeof(fail));
memset(sta,0,sizeof(sta));
}
}ac;
signed main(){
freopen("square.in","r",stdin);
freopen("square.out","w",stdout);
scanf("%lld",&T);yg['D'-'A']=0;yg['R'-'A']=1;
while(T--){
ac.clear();ans=0;
scanf("%lld%lld",&m,&n);
scanf("%s",a+1);an=strlen(a+1);ac.ins(1);
scanf("%s",a+1);an=strlen(a+1);ac.ins(2);
ac.build();ac.get_ans();
printf("%lld\n",ans);
}
}
T4 舞动的夜晚
这个其实可以直接暴力网络流,删一个点再跑一遍
但是正解是\(网络流+tarjan\)
直接在网络流的残量网络上跑\(tarjan\)
如果一条边的两个点在同一个联通分量里或者在最大流上,这个就是可以的
AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=200005;
const int M=1000005;
const int inf=0x3f3f3f3f;
int n,m,T;
int fu[M],tu[M];
int fr[M*2],to[M*2],nxt[M*2],val[M*2],id[M*2],head[N],hea[N],rp=1;
void add_edg(int x,int y,int z,int i){
to[++rp]=y;
fr[rp]=x;id[rp]=i;
val[rp]=z;
nxt[rp]=head[x];
head[x]=rp;
}
int s=20001,t=20002,ans,ji[M];
int dep[N];
bool bfs(){
memcpy(head,hea,sizeof(head));
memset(dep,0x3f,sizeof(dep));
queue<int> q;while(!q.empty())q.pop();
q.push(s);dep[s]=0;
while(!q.empty()){
int x=q.front();q.pop();
for(int i=head[x];i;i=nxt[i]){
int y=to[i];
if(!val[i]||dep[y]<=dep[x]+1)continue;
q.push(y);dep[y]=dep[x]+1;
if(y==t)return true;
}
}
return false;
}
int dfs(int x,int in){
if(x==t)return in;
int rst=in,go;
for(int i=head[x];i;head[x]=i=nxt[i]){
int y=to[i];
if(!val[i]||dep[y]!=dep[x]+1)continue;
go=dfs(y,min(val[i],rst));
if(go)rst-=go,val[i]-=go,val[i^1]+=go;
else dep[y]=0;
if(!rst)break;
}return in-rst;
}
void dinic(){
memcpy(hea,head,sizeof(hea));
int ret=0;
while(bfs())ret+=dfs(s,inf);
}
int dfn[N],low[N],cnt,bel[N],tot;
stack<int> sta;
void tarjan(int x){
dfn[x]=low[x]=++cnt;
sta.push(x);
for(int i=head[x];i;i=nxt[i]){
int y=to[i];
if(!dfn[y])tarjan(y),low[x]=min(low[x],low[y]);
else if(!bel[y])low[x]=min(low[x],low[y]);
}
if(dfn[x]==low[x]){
bel[x]=++tot;
while(x!=sta.top())bel[sta.top()]=tot,sta.pop();
sta.pop();
}
}
bool vis[M];
signed main(){
#ifdef oj
freopen("night.in","r",stdin);
freopen("night.out","w",stdout);
#endif
scanf("%d%d%d",&n,&m,&T);
fo(i,1,T){
scanf("%d%d",&fu[i],&tu[i]);tu[i]+=n;
add_edg(fu[i],tu[i],1,0);
add_edg(tu[i],fu[i],0,i);
}
fo(i,1,n)add_edg(s,i,1,0),add_edg(i,s,0,0);
fo(i,1,m)add_edg(i+n,t,1,0),add_edg(t,i+n,0,0);
dinic();
int nn=rp;memset(head,0,sizeof(head));rp=1;
fo(i,1,nn)if(val[i])add_edg(fr[i],to[i],1,id[i]),vis[id[i]]=true;//cout<<fr[i]<<" "<<to[i]<<endl;
fo(i,1,n+m)if(!dfn[i])tarjan(i);//cout<<i<<" ";cout<<endl;
fo(i,1,T)if(bel[fu[i]]!=bel[tu[i]]&&!vis[i])ji[++ans]=i;
//fo(i,1,n+m)cout<<i<<" "<<bel[i]<<endl;
printf("%d\n",ans);
fo(i,1,ans)printf("%d ",ji[i]);
}