【比賽報告】2018.10.28牛客網線上賽[牛客OI周賽3-提高組] NOIP練習賽卷二十三
阿新 • • 發佈:2018-11-01
A.地鬥主 矩陣快速冪
#include<cstdio> #include<cstring> typedef long long ll; int n,m,t,ans[5]={0,1,5,11,36}; struct Matrix{ ll a[5][5]; }; Matrix operator *(Matrix a,Matrix b) { Matrix c;memset(c.a,0,sizeof(c.a)); for(int i=0;i<=3;i++) for(int j=0;j<=3;j++) { for(int k=0;k<=3;k++) c.a[i][j]+=a.a[i][k]*b.a[k][j]; c.a[i][j]%=m; } return c; } Matrix operator ^(Matrix a,int p) { Matrix c;memset(c.a,0,sizeof(c.a)); for(int i=0;i<=3;i++)c.a[i][i]=1;//單位矩陣 while(p) { if(p&1)c=c*a; a=a*a; p>>=1; } return c; } int main() { //freopen("in.txt","r",stdin); scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); if(n<=4)printf("%d\n",ans[n]); else { Matrix a,b,c;memset(a.a,0,sizeof(a.a));memset(b.a,0,sizeof(b.a)); a.a[0][0]=1;a.a[0][1]=5;a.a[0][2]=1;a.a[0][3]=-1;a.a[1][0]=1;a.a[2][1]=1;a.a[3][2]=1; b.a[0][0]=36;b.a[1][0]=11;b.a[2][0]=5;b.a[3][0]=1; c=a^(n-4); c=c*b; printf("%lld\n",(c.a[0][0]+m)%m); } } return 0; }
總結
公式推導與矩陣快速冪好題
B.1408 線性DP
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int N=2e3+10; int n,dp[N][N],w[N],b[N],c1[N][N],c2[N][N]; int main() { //freopen("in.txt","r",stdin); cin>>n; for(int i=1,x;i<=2*n;i++) { char ch; cin>>ch>>x; if(ch=='W')w[x]=i; else b[x]=i; } for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) { c1[i][n]+=(w[j]<w[i]); c2[i][n]+=(b[j]<b[i]); } for(int i=1;i<=n;i++) for(int j=n-1;j>=0;j--) { c1[i][j]=c1[i][j+1]+(b[j+1]<w[i]); c2[i][j]=c2[i][j+1]+(w[j+1]<b[i]); } memset(dp,0x3f,sizeof(dp));dp[0][0]=0; for(int i=0;i<=n;i++) for(int j=0;j<=n;j++) { if(i==0&&j==0)continue; if(i>0)dp[i][j]=min(dp[i][j],dp[i-1][j]+c1[i][j]); if(j>0)dp[i][j]=min(dp[i][j],dp[i][j-1]+c2[j][i]); } printf("%d\n",dp[n][n]); return 0; }
總結
線性DP好題
C.爆瓶子 二分圖匹配
學習了大佬程式碼,畢竟本蒟蒻至今寫不來二分圖匹配(NOIP退役預定qwq)
#include<cstdio> #include<cstring> const int N=500; int t,n,m,g[N][N],hd[N],tot,To[N],match[N],vis[N],lim; struct Edge{ int v,nx; }e[N*N]; inline void add(int u,int v) { e[++tot].v=v; e[tot].nx=hd[u]; hd[u]=tot; } inline int dfs(int x) { for(int i=hd[x];i;i=e[i].nx) if(!vis[e[i].v]) { vis[e[i].v]=1;//不要在外面寫vis[x]=1,x上沒有vis標記 if(!match[e[i].v]||(match[e[i].v]>lim&&dfs(match[e[i].v]))) { match[e[i].v]=x; To[x]=e[i].v; return 1; } } return 0; } inline void solve() { memset(hd,0,sizeof(hd));tot=0; for(int i=1;i<=n;i++) for(int j=n;j>=1;j--) if(g[i][j])add(i,j+n); //左邊的點編號1~n //右邊的點編號n+1~2n memset(To,0,sizeof(To)); memset(match,0,sizeof(match)); for(int i=1;i<=n;i++) { memset(vis,0,sizeof(vis)); if(!To[i])lim=0,dfs(i); } for(int i=1;i<=n;i++) { memset(vis,0,sizeof(vis)); for(int j=hd[i];j;j=e[j].nx) { if(To[i]==e[j].v)break;//沒有找到更小的解 if(match[e[j].v]<i)continue;//已與先前匹配 int x=match[e[j].v],y=To[i]; To[x]=0;match[y]=0;To[i]=e[j].v;match[e[j].v]=i; if(lim=i,dfs(x))break;//lim表示不能增廣到確定匹配的i-e[j].v To[x]=e[j].v;match[e[j].v]=x;To[i]=y;match[y]=i; } } } int main() { //freopen("in.txt","r",stdin); scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) g[i][j]=1; for(int i=1;i<=m;i++) for(int j=1,x;j<=n;j++) { scanf("%d",&x); g[j][x]=0; } for(int i=1;i<=n-m;i++) { solve(); for(int j=1;j<=n;j++) printf("%d ",To[j]-n),g[j][To[j]-n]=0; puts(""); } } return 0; }
總結
二分圖匹配+字典序最小
比賽總結
無