noip2015
NOIP 2015
Day1
T1 P2615 [NOIP2015 提高组] 神奇的幻方
解题方法在题面里都告诉了,模拟就完事。
#include<iostream> using namespace std; const int mm=40; int m[mm][mm]; int i,j,n,k; int main() { cin>>n; for(i=0; i<=n+1; i++) { for(j=0; j<=n+1; j++) { if(i>0&&i<=n&&j>0&&j<=n) m[i][j]==0; else m[i][j]==1; } } m[1][n/2+1]=1; i=1; j=n/2+1; k=2; while(k<=n*n) { if(i==1&&j!=n) { m[n][j+1]=k; i=n; j=j+1; k++; continue; } else if(i!=1&&j==n) { m[i-1][1]=k; j=1; i=i-1; k++; continue; } else if(i==1&&j==n) { m[i+1][j]=k; i=i+1; k++; continue; } else if(i!=1&&j!=n) { //cout<<"emm"; if(m[i-1][j+1]==0) { m[i-1][j+1]=k; i-=1; j+=1; k++; continue; } else { m[i+1][j]=k; i+=1; k++; continue; } } /* for(i=1; i<=n; i++) { for(j=1; j<=n; j++) { cout<<m[i][j]; } cout<<endl; } cout<<k<<' '<<endl;*/ } for(i=1; i<=n; i++) { for(j=1; j<=n; j++) { cout<<m[i][j]<<' '; } cout<<endl; } return 0; }
T2 P2661 [NOIP2015 提高组] 信息传递
一道求最小环的问题,且每个点只有一个出边,可以用带权并查集解决(并非通法)。
#include<bits/stdc++.h> using namespace std; int ans,n,t[200001],f[200001],s[200001]; int fa(int a) { if(f[a]!=a) { int ff=f[a]; f[a]=fa(f[a]); s[a]+=s[ff]; } return f[a]; } void cir(int a,int b) { int x=fa(a),y=fa(b); if(x==y) ans=min(ans,s[a]+s[b]+1); else f[x]=y,s[a]=s[b]+1; } int main() { ans=9999999; cin>>n; for(int i=1;i<=n;i++) f[i]=i; for(int i=1;i<=n;i++) { cin>>t[i]; cir(i,t[i]); } cout<<ans; return 0; }
T3 P2668 [NOIP2015 提高组] 斗地主
此处省略一万行模拟,你懂的。
#include<cstdio> #include<algorithm> #include<cstring> #define mxh 1000000007 using namespace std; int ans,T,n,i,x,y,pai[6],cnt[15]; void find(int step){ int i,j,k,w; if(step>=ans)return; ans=min(ans,step+pai[1]+pai[2]+pai[3]+pai[4]); if(pai[4])for(i=2;i<=14;i++)if(cnt[i]==4){ pai[4]--;cnt[i]-=4; if(!pai[3]&&!pai[4]&&!pai[1]&&pai[2]<=1){ ans=min(ans,step+1); pai[4]++;cnt[i]+=4; return; } for(j=1;j<=14;j++)if(cnt[j]){ pai[cnt[j]]--;cnt[j]--;pai[cnt[j]]++; for(k=j;k<=14;k++)if(cnt[k]){ pai[cnt[k]]--;cnt[k]--;pai[cnt[k]]++; find(step+1); pai[cnt[k]]--;cnt[k]++;pai[cnt[k]]++; } pai[cnt[j]]--;cnt[j]++;pai[cnt[j]]++; } if(pai[2]||pai[3]||pai[4])for(j=2;j<=14;j++)if(cnt[j]>1){ pai[cnt[j]]--;cnt[j]-=2;pai[cnt[j]]++; for(k=j;k<=14;k++)if(cnt[k]>1){ pai[cnt[k]]--;cnt[k]-=2;pai[cnt[k]]++; find(step+1); pai[cnt[k]]--;cnt[k]+=2;pai[cnt[k]]++; } pai[cnt[j]]--;cnt[j]+=2;pai[cnt[j]]++; } pai[4]++;cnt[i]+=4; } if(pai[3])for(i=2;i<=14;i++)if(cnt[i]>=3){ pai[cnt[i]]--;cnt[i]-=3;pai[cnt[i]]++; find(step+1); for(j=1;j<=14;j++)if(cnt[j]){ pai[cnt[j]]--;cnt[j]--;pai[cnt[j]]++; find(step+1); pai[cnt[j]]--;cnt[j]++;pai[cnt[j]]++; } if(pai[2]||pai[3]||pai[4])for(j=2;j<=14;j++)if(cnt[j]>1){ pai[cnt[j]]--;cnt[j]-=2;pai[cnt[j]]++; find(step+1); pai[cnt[j]]--;cnt[j]+=2;pai[cnt[j]]++; } pai[cnt[i]]--;cnt[i]+=3;pai[cnt[i]]++; } if(pai[3]+pai[4]>=2)for(i=3;i<14;i++)if(cnt[i]>=3&&cnt[i+1]>=3){ pai[cnt[i]]--;cnt[i]-=3;pai[cnt[i]]++;w=i; for(j=i+1;cnt[j]>=3&&j<=14;j++){ pai[cnt[j]]--;cnt[j]-=3;pai[cnt[j]]++; find(step+1);w=j; } for(j=i;j<=w;j++){ pai[cnt[j]]--;cnt[j]+=3;pai[cnt[j]]++; } } if(pai[2]+pai[3]+pai[4]>=3)for(i=3;i<13;i++)if(cnt[i]>=2&&cnt[i+1]>=2&&cnt[i+2]>=2){ pai[cnt[i]]--;cnt[i]-=2;pai[cnt[i]]++; pai[cnt[i+1]]--;cnt[i+1]-=2;pai[cnt[i+1]]++;w=i+1; for(j=i+2;cnt[j]>=2&&j<=14;j++){ pai[cnt[j]]--;cnt[j]-=2;pai[cnt[j]]++; find(step+1);w=j; } for(j=i;j<=w;j++){ pai[cnt[j]]--;cnt[j]+=2;pai[cnt[j]]++; } } if(pai[1]+pai[2]+pai[3]+pai[4]>=5)for(i=3;i<11;i++)if(cnt[i]&&cnt[i+1]&&cnt[i+2]&&cnt[i+3]&&cnt[i+4]){ pai[cnt[i]]--;cnt[i]--;pai[cnt[i]]++; pai[cnt[i+1]]--;cnt[i+1]--;pai[cnt[i+1]]++; pai[cnt[i+2]]--;cnt[i+2]--;pai[cnt[i+2]]++; pai[cnt[i+3]]--;cnt[i+3]--;pai[cnt[i+3]]++;w=i+3; for(j=i+4;cnt[j]&&j<=14;j++){ pai[cnt[j]]--;cnt[j]--;pai[cnt[j]]++; find(step+1);w=j; } for(j=i;j<=w;j++){ pai[cnt[j]]--;cnt[j]++;pai[cnt[j]]++; } } } int main(){ for(scanf("%d%d",&T,&n);T--;){ ans=12; memset(cnt,0,sizeof(cnt)); memset(pai,0,sizeof(pai)); for(i=1;i<=n;i++){ scanf("%d%d",&x,&y); if(x==1)x=14; if(x==0)x=1; cnt[x]++; } for(i=1;i<=14;i++)pai[cnt[i]]++; find(0); printf("%d\n",ans); } }
Day2
T1 P2678 [NOIP2015 提高组] 跳石头
显然,我们当前所在的石头越靠前,越容易拉大距离,所以去掉石头直到距离下块石头足够远,然后就跳到下一块,这样贪心便可得到能否使最短距离达到当前的mid,二分答案的前提成立。
#include<bits/stdc++.h> using namespace std; long L,n,m,a[50010]; bool check(int k) { long rem=0,now=0; for(int i=1;i<=n+1;i++) { if((a[i]-a[now])<k) rem++; else now=i; } if(rem>m) return 0; return 1; } int main() { //freopen("2678.in","r",stdin); cin>>L>>n>>m; for(int i=1;i<=n;i++) { cin>>a[i]; } a[n+1]=L; long l=0,r=1000000001,m; while(r-l>1) { m=(l+r)/2; if(check(m)) l=m; else r=m; } cout<<l; return 0; }
T2 P2679 [NOIP2015 提高组] 子串
dp。
思路,显然至少要两维表示a串和b串的前i,j位,然后又要一维k记录分几段。f[i][j][k]表示a串前i位分成k段构成b的方案数。
考虑转移,结果发现无法转移新的点是否与上一块相连,所以再加一维表示最后一点是否被使用。
转移:
f[i][j][k][0]=f[i-1][j-1][k][0]+f[i-1][j-1][k][1]
若 a[i]==b[i] f[i][j][k][1]=f[i-1][j-1][k][1]+f[i-1][j-1][k-1][0]+f[i-1][j-1][k-1][1]
初始化:
枚举a[i=1~n] a[i]=b[1] f[a][1][1][1]=1
#include<bits/stdc++.h> using namespace std; int mod=1000000007; int n,m,t,s,now,pre; int f[2][201][201][2]; string s1,s2; int main() { cin>>n>>m>>t; cin>>s1>>s2;s1=' '+s1;s2=' '+s2; now=0,pre=1; for(int i=1;i<=n;i++) { swap(now,pre); f[now][1][1][0]=s; if(s1[i]==s2[1]) f[now][1][1][1]=1,s++; for(int j=2;j<=m;j++) { for(int k=1;k<=t;k++) { if(s1[i]==s2[j]) f[now][j][k][1]=((f[pre][j-1][k-1][1]+f[pre][j-1][k][1])%mod+f[pre][j-1][k-1][0])%mod; f[now][j][k][0]=(f[pre][j][k][0]+f[pre][j][k][1])%mod; } } for(int j=1;j<=m;j++) for(int k=1;k<=t;k++) f[pre][j][k][1]=f[pre][j][k][0]=0; } cout<<(f[now][m][t][1]+f[now][m][t][0])%mod; return 0; }
T3 P2680 [NOIP2015 提高组] 运输计划
二分+求链长+贪心
首先答案是符合单调性的所以首先想到二分答案,其实想到二分答案这道题就已经解决一半了。有二分就有check函数,首先求出每条链长(lca,树链剖分)找所有长度大于mid的,用差分求出被所有链都经过的权值最大的边,若不存在这样的边就返回false,存在则返回最长链-该边权值是否大于mid。
#include<bits/stdc++.h> using namespace std; const int MM=600005; int ccnt,nnum[MM],cnt,tot,_max,x,b,u,v,w,n,m,nxt[MM],cf[MM],head[MM],to[MM],dis[MM],dep[MM],f[MM][26],d[MM][26],D[MM]; int l,r,mid,s[MM],t[MM]; void add(int u,int v,int w) { nxt[++tot]=head[u]; head[u]=tot; to[tot]=v; dis[tot]=w; } void dfs(int now,int fat,int deep) { ccnt++; nnum[ccnt]=now; dep[now]=deep; f[now][0]=fat; for(int i=head[now];i;i=nxt[i]) { if(to[i]==fat) { d[now][0]=dis[i]; continue; } dfs(to[i],now,deep+1); } } void build(int now) { for(int i=1;i<=25;i++) d[now][i]=d[now][i-1]+d[f[now][i-1]][i-1],f[now][i]=f[f[now][i-1]][i-1]; for(int i=head[now];i;i=nxt[i]) if(to[i]!=f[now][0]) build(to[i]); } int getlca(int a,int b) { if(dep[b]>dep[a]) swap(a,b); for(int i=25;i>=0;i--) if(f[a][i]&&dep[f[a][i]]>=dep[b]) a=f[a][i]; if(a==b) return a; for(int i=25;i>=0;i--) if(f[a][i]!=f[b][i]&&f[a][i]&&f[b][i]) a=f[a][i],b=f[b][i]; return f[a][0]; } int getdis(int a,int b) { int tmp=0; if(dep[b]>dep[a]) swap(a,b); for(int i=25;i>=0;i--) if(f[a][i]&&dep[f[a][i]]>=dep[b]) tmp+=d[a][i],a=f[a][i]; if(a==b) return tmp; for(int i=25;i>=0;i--) if(f[a][i]!=f[b][i]&&f[a][i]&&f[b][i]) tmp+=d[a][i],tmp+=d[b][i],a=f[a][i],b=f[b][i]; tmp+=d[a][0]+d[b][0]; return tmp; } void sum(int now,int x) { for(int i=head[now];i;i=nxt[i]) if(to[i]!=f[now][0]) sum(to[i],x),cf[now]+=cf[to[i]]; if(cf[now]==cnt&&d[now][0]>=_max-x) b=1; } bool check(int x) { cnt=0;b=0; memset(cf,0,sizeof(cf)); for(int i=1;i<=m;i++) if(D[i]>x) cf[s[i]]++,cf[t[i]]++,cf[getlca(s[i],t[i])]-=2,cnt++; if(cnt==0) return 1; for(int i=n;i>=1;i--) cf[f[nnum[i]][0]]+=cf[nnum[i]]; for(int i=2;i<=n;i++) if(cf[i]==cnt&&d[i][0]>=_max-x) return 1; return 0; } int main() { cin>>n>>m; for(int i=1;i<=n-1;i++) { cin>>u>>v>>w; add(u,v,w); add(v,u,w); } dfs(1,0,1); build(1); for(int i=1;i<=m;i++) cin>>s[i]>>t[i],D[i]=getdis(s[i],t[i]),_max=max(D[i],_max); l=0,r=_max+100; while(r>l) { mid=(l+r)>>1; if(check(mid)) r=mid; else l=mid+1; } cout<<l; return 0; }