$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 }
代码戳这里

 

posted @ 2019-11-07 20:25  小叽居biubiu  阅读(149)  评论(0编辑  收藏  举报