$NOIP2015$ 题解报告
目录
$Luogu\ P2615$ 神奇的幻方$(\ √\ )$
$Luogu\ P2661$ 信息传递$(\ √\ )$
$Luogu\ P2668$ 斗地主$(\ √\ )$
$Luogu\ P2678$ 跳石头$(\ √\ )$
$Luogu\ P2679$ 子串$(\ √\ )$
$Luogu\ P2680$ 运输计划$(\ √\ )$
$Luogu\ P2615$ 神奇的幻方
昂直接模拟就好了
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n; 4 int square[40][40]; 5 struct Num{ 6 int x,y; 7 }num[40*40]; 8 void work(int k){ 9 if(num[k-1].x==1&&num[k-1].y!=n) 10 num[k].x=n,num[k].y=num[k-1].y+1; 11 if(num[k-1].y==n&&num[k-1].x!=1) 12 num[k].x=num[k-1].x-1,num[k].y=1; 13 if(num[k-1].x==1&&num[k-1].y==n) 14 num[k].x=2,num[k].y=num[k-1].y; 15 if(num[k-1].x!=1&&num[k-1].y!=n){ 16 if(square[num[k-1].x-1][num[k-1].y+1]==0) 17 num[k].x=num[k-1].x-1,num[k].y=num[k-1].y+1; 18 else 19 num[k].x=num[k-1].x+1,num[k].y=num[k-1].y; 20 } 21 square[num[k].x][num[k].y]=k; 22 return; 23 } 24 int main(){ 25 cin>>n; 26 square[1][n/2+1]=1; 27 num[1].x=1; 28 num[1].y=n/2+1; 29 for(int i=2;i<=n*n;i++) 30 work(i); 31 for(int i=1;i<=n;i++){ 32 for(int j=1;j<=n;j++) 33 cout<<square[i][j]<<" "; 34 cout<<endl; 35 } 36 }
$Luogu\ P2661$ 信息传递
并查集求最小环,如果两个点在一个集合里,那么就构成一个环,长度为两个点到这个集合的父亲节点的距离加上$1$
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=200002; 4 int n,fa[N],dis[N],minn; 5 int find(int x){ 6 if(fa[x]!=x){ 7 int last=fa[x]; 8 fa[x]=find(fa[x]); 9 dis[x]+=dis[last]; 10 } 11 return fa[x]; 12 } 13 void check(int x,int y){ 14 int fx=find(x),fy=find(y); 15 if(fx!=fy) {fa[fx]=fy;dis[x]=dis[y]+1;} 16 else minn=min(minn,dis[x]+dis[y]+1); 17 return; 18 } 19 int main(){ 20 int i,T; 21 scanf("%d",&n); 22 for(i=1;i<=n;i++) 23 fa[i]=i; 24 minn=N*2; 25 for(i=1;i<=n;i++){ 26 scanf("%d",&T); 27 check(i,T); 28 } 29 printf("%d",minn); 30 return 0; 31 }
$Luogu\ P2668$ 斗地主
大概是贪心$+$模拟$+dfs$?去年做的了,其实挺恶心
花色没什么用,我们只需要记录数量。$dfs$过程中,贪心先出顺子,单顺子双顺子三顺子;然后出三带一三带二或者四带二,这里注意就是如果有四张牌先试一下三带一和三带二;最后只要有牌都可以一次把这种牌出完,统计一下答案最小值即可
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define mem(a,b) memset(a,b,sizeof(a)); 4 int n,T,ans; 5 int num[20]; 6 void search(int times){ 7 if(times>=ans) return; 8 int len=0; 9 for(int i=3;i<=14;i++){ 10 if(num[i]==0) {len=0;continue;} 11 else { 12 len++; 13 if(len>=5){ 14 for(int j=i;j>=i-len+1;j--) 15 num[j]--; 16 search(times+1); 17 for(int j=i;j>=i-len+1;j--) 18 num[j]++; 19 } 20 } 21 } 22 len=0; 23 for(int i=3;i<=14;i++){ 24 if(num[i]<2) {len=0;continue;} 25 else{ 26 len++; 27 if(len>=3){ 28 for(int j=i;j>=i-len+1;j--) 29 num[j]-=2; 30 search(times+1); 31 for(int j=i;j>=i-len+1;j--) 32 num[j]+=2; 33 } 34 } 35 } 36 len=0; 37 for(int i=3;i<=14;i++){ 38 if(num[i]<3) {len=0;continue;} 39 else{ 40 len++; 41 if(len>=2){ 42 for(int j=i;j>=i-len+1;j--) 43 num[j]-=3; 44 search(times+1); 45 for(int j=i;j>=i-len+1;j--) 46 num[j]+=3; 47 } 48 } 49 } 50 for(int i=2;i<=14;i++){ 51 if(num[i]<=2) continue; 52 if(num[i]==3){ 53 num[i]-=3; 54 for(int j=2;j<=15;j++){ 55 if(num[j]==0||i==j) continue; 56 num[j]--; 57 search(times+1); 58 num[j]++; 59 } 60 for(int j=2;j<=14;j++){ 61 if(num[j]<2||i==j) continue; 62 num[j]-=2; 63 search(times+1); 64 num[j]+=2; 65 } 66 num[i]+=3; 67 } 68 else{ 69 num[i]-=3; 70 for(int j=2;j<=15;j++){ 71 if(num[j]==0||i==j) continue; 72 num[j]--; 73 search(times+1); 74 num[j]++; 75 } 76 for(int j=2;j<=14;j++){ 77 if(num[j]<2||i==j) continue; 78 num[j]-=2; 79 search(times+1); 80 num[j]+=2; 81 } 82 num[i]--; 83 for(int j=2;j<=15;j++){ 84 if(num[j]==0||i==j) continue; 85 num[j]--; 86 for(int k=2;k<=15;k++){ 87 if(num[k]==0||i==k) continue; 88 num[k]--; 89 search(times+1); 90 num[k]++; 91 } 92 num[j]++; 93 } 94 for(int j=2;j<=14;j++){ 95 if(num[j]<2||i==j) continue; 96 num[j]-=2; 97 for(int k=2;k<=14;k++){ 98 if(num[k]<2||i==k) continue; 99 num[k]-=2; 100 search(times+1); 101 num[k]+=2; 102 } 103 num[j]+=2; 104 } 105 num[i]+=4; 106 } 107 } 108 for(int i=2;i<=15;i++) 109 if(num[i]) times++; 110 ans=min(ans,times); 111 } 112 int main(){ 113 scanf("%d%d",&T,&n); 114 while(T--){ 115 mem(num,0); 116 ans=30; 117 int a,b; 118 for(int i=1;i<=n;i++){ 119 scanf("%d%d",&a,&b); 120 if(a==0) a=15; 121 if(a==1) a=14; 122 num[a]++; 123 } 124 search(0); 125 printf("%d\n",ans); 126 } 127 }
$Luogu\ P2678$ 跳石头
二分答案,然后$check$过程中求出能留下的石子数
1 #include<bits/stdc++.h> 2 using namespace std; 3 int l,n,m,d[50002]; 4 int fr(){ 5 int w=0,q=1; 6 char ch=getchar(); 7 while((ch<'0'||ch>'9')&&ch!='-') ch=getchar(); 8 if(ch=='-') q=-1; 9 while(ch<='9'&&ch>='0') w=w*10+ch-'0',ch=getchar(); 10 return w*q; 11 } 12 int maxn=0; 13 bool pd(int x){ 14 int now=0,num=0,next=0; 15 while(next<n+1){ 16 next++; 17 if(d[next]-d[now]<x) 18 num++; 19 else now=next; 20 } 21 //cout<<"x="<<x<<" num="<<num<<endl; 22 if(num>m) return 0; 23 else return 1; 24 } 25 int main(){ 26 l=fr();n=fr();m=fr(); 27 for(int i=1;i<=n;i++) 28 d[i]=fr(); 29 d[n+1]=l; 30 int L=1,R=l; 31 while(L<=R){ 32 int mid=(L+R)/2; 33 //cout<<"mid="<<mid<<endl; 34 if(pd(mid)){ 35 L=mid+1; 36 maxn=mid; 37 //cout<<mid<<" "<<maxn<<endl; 38 } 39 else R=mid-1; 40 //cout<<"L="<<L<<" R="<<R<<endl; 41 } 42 printf("%d",maxn); 43 return 0; 44 }
$Luogu\ P2679$ 子串
$DP$,我还有点地方没搞懂,缓一缓先$QAQ$
1 #include<bits/stdc++.h> 2 #define ri register int 3 #define ll long long 4 #define rl register ll 5 #define go(i,a,b) for(ri i=a;i<=b;i++) 6 #define back(i,a,b) for(ri i=a;i>=b;i--) 7 #define g() getchar() 8 #define il inline 9 #define pf printf 10 #define mem(a,b) memset(a,b,sizeof(a)) 11 using namespace std; 12 il int fr(){ 13 ri w=0,q=1;char ch=g(); 14 while(ch<'0'||ch>'9'){if(ch=='-')q=-1;ch=g();} 15 while(ch>='0'&&ch<='9')w=(w<<1)+(w<<3)+ch-'0',ch=g(); 16 return w*q; 17 } 18 const int M=202; 19 const int N=1002; 20 const int mod=1000000007; 21 int n,m,K; 22 ll f[M][M],s[M][M]; 23 char a[N],b[M]; 24 int main(){ 25 //freopen(".in","r",stdin); 26 //freopen(".out","w",stdout); 27 n=fr();m=fr();K=fr();f[0][0]=1; 28 scanf("%s",a);scanf("%s",b); 29 go(i,1,n)back(j,m,1)back(k,K,1)//这儿为什么是倒序昂?QAQ 30 f[j][k]=(f[j][k]+(s[j][k]=(a[i-1]==b[j-1]?s[j-1][k]+f[j-1][k-1]:0)))%mod; 31 pf("%lld\n",f[m][K]); 32 return 0; 33 }
$Luogu\ P2680$ 运输计划
首先预处理出每条路径的两个端点,长度和$lca$,然后二分答案。$check$的时候把每个长度大于$mid$的路径利用树上差分求出每条边经过了几次,其中$dif[i]$表示的是$i$节点与其父亲之间的边的值。然后如果有一条边满足所有大于$mid$的路径都经过了,并且这条路径的边权$\ge$最长的路径长度$-mid$,这个$mid$就是合法的
1 #include<bits/stdc++.h> 2 #define ri register int 3 #define ll long long 4 #define rl register ll 5 #define go(i,a,b) for(ri i=a;i<=b;i++) 6 #define back(i,a,b) for(ri i=a;i>=b;i--) 7 #define g() getchar() 8 #define il inline 9 #define pf printf 10 #define mem(a,b) memset(a,b,sizeof(a)) 11 #define E(i,x) for(ri i=hd[x];i;i=e[i].nxt) 12 #define t(i) e[i].to 13 using namespace std; 14 il int fr(){ 15 ri w=0,q=1;char ch=g(); 16 while(ch<'0'||ch>'9'){if(ch=='-')q=-1;ch=g();} 17 while(ch>='0'&&ch<='9')w=(w<<1)+(w<<3)+ch-'0',ch=g(); 18 return w*q; 19 } 20 const int N=300002; 21 const int INF=1e9+7; 22 int n,m,hd[N],ed,dif[N],f[N][20],dep[N]; 23 int ans,l,r,dis[N]; 24 struct edge{ 25 int nxt,to,w; 26 }e[N<<1]; 27 struct line{ 28 int lca,x,y,len; 29 }L[N]; 30 il void build(int x,int y,int w){e[++ed]=(edge){hd[x],y,w};hd[x]=ed;return;} 31 il void Tree(int x,int fa){ 32 f[x][0]=fa;dep[x]=dep[fa]+1; 33 go(i,1,19)f[x][i]=f[f[x][i-1]][i-1]; 34 E(i,x){ 35 if(t(i)==fa)continue; 36 dis[t(i)]=dis[x]+e[i].w;Tree(t(i),x); 37 } 38 return; 39 } 40 il int LCA(int x,int y){ 41 if(dep[x]<dep[y])swap(x,y); 42 back(i,19,0)if(dep[f[x][i]]>=dep[y])x=f[x][i]; 43 if(x==y)return x; 44 back(i,19,0)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; 45 return f[x][0]; 46 } 47 il void dfs(int x,int fa){ 48 E(i,x){ 49 if(t(i)==fa)continue; 50 dfs(t(i),x);dif[x]+=dif[t(i)]; 51 } 52 return; 53 } 54 il bool check(int x){ 55 mem(dif,0);int num=0,mx=0; 56 go(i,1,m){ 57 if(L[i].len<=x)continue;num++;mx=max(mx,L[i].len-x); 58 dif[L[i].x]++;dif[L[i].y]++;dif[L[i].lca]-=2; 59 } 60 dfs(1,0); 61 //go(i,1,n)cout<<"dif="<<dif[i]<<" dis="<<dis[i]-dis[f[i][0]]<<endl; 62 go(i,2,n)if(dif[i]==num&&dis[i]-dis[f[i][0]]>=mx)return 1; 63 return 0; 64 } 65 int main(){ 66 //freopen(".in","r",stdin); 67 //freopen(".out","w",stdout); 68 n=fr();m=fr(); 69 go(i,1,n-1){ 70 int x=fr(),y=fr(),w=fr(); 71 build(x,y,w);build(y,x,w); 72 } 73 Tree(1,0); 74 go(i,1,m){ 75 int x=fr(),y=fr(); 76 L[i].lca=LCA(x,y);L[i].x=x;L[i].y=y; 77 L[i].len=dis[x]+dis[y]-2*dis[L[i].lca]; 78 r=max(r,L[i].len); 79 } 80 while(l<=r){ 81 int mid=(l+r)>>1; 82 //cout<<"l="<<l<<" r="<<r<<" mid="<<mid<<endl; 83 if(check(mid))ans=mid,r=mid-1; 84 else l=mid+1; 85 } 86 pf("%d\n",ans); 87 return 0; 88 }