Loading

Noip模拟35 2021.8.10

考试题目变成四道了,貌似确实根本改不完。。。

不过给了两个小时颓废时间确实很爽(芜湖~~)

但是前几天三道题改着不是很费劲的时候为什么不给放松时间,

非要在改不完题的时候颓??

算了算了不碎碎念了。。

T1 玩游戏

好多大神在考场上使用乱搞做法$A$掉了这道题,但是我只水了$20$就跑去刚$T2$了

但是大神们的做法会被其他的恶心数据卡掉,样例是随的所以飞快。。

正解是比较$diao$的双指针。记录五个变量:

$sum,sum1,sum2,max1,max2$分别表示$l-r$的和,$k-l$的和,$k-r$的和,$k-l$之间的最大值,$k-r$之间的最大值。

从$k$开始分别先找到使$sum1,sum2$小于$0$的点,记录下$pos1,pos2,max1,max2$

然后开始跳指针,判断条件是整个的$l-r$是否可以跨过记录的两个坎——$max1,max2$

能跨过就加上,再进行跳指针,不断的跳。。最后能到两端就返回就行。

当然会出现左右都跨不过坎的情况,直接$break$掉,不用害怕,还没跳完。

记录最后两个指针跳到的位置,看看从两边向中间跳能不能跳到刚才过不去的位置。

类似上述的操作再来一边就行。复杂度$O(n)$,非常优秀了。

代码还有一些细节,类似读入的时候不用管$a[1]$,因为根本用不到。

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 using namespace std;
 4 inline int read(){
 5     int x=0,f=1; char ch=getchar();
 6     while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
 7     while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar();}
 8     return x*f;
 9 }
10 const int inf=1e18;
11 int T,n,k,a[100005],l,r;
12 namespace WSN{
13     inline bool wsn(int l,int r){
14         int sum=0,sum1=0,sum2=0;
15         int max1=-inf,max2=-inf;
16         int pos1,pos2;
17         for(int i=l-1;i>=0;i--){
18             sum1+=a[i]; max1=max(max1,sum1);
19             if(sum1<=0) {pos1=i;break;}
20         }
21         for(int i=r+1;i<=n+1;i++){
22             sum2+=a[i]; max2=max(max2,sum2);
23             if(sum2<=0) {pos2=i;break;}
24         }
25         while(1){
26             if(l==1&&r==n) return 1;
27             if(sum+max1<=0&&pos1){
28                 sum+=sum1; sum1=0; l=pos1; max1=-inf;
29                 for(int i=l-1;i>=0;i--){
30                     sum1+=a[i]; max1=max(max1,sum1);
31                     if(sum1<=0) {pos1=i;break;}
32                 }
33             }
34             else if(sum+max2<=0&&pos2<=n){
35                 sum+=sum2; sum2=0; r=pos2; max2=-inf;
36                 for(int i=r+1;i<=n+1;i++){
37                     sum2+=a[i]; max2=max(max2,sum2);
38                     if(sum2<=0) {pos2=i;break;}
39                 }
40             }
41             else break;
42         }
43         if(l>r) return 0;
44         sum=0; for(int i=1;i<=n;i++) sum+=a[i],a[i]=-a[i];
45         if(sum>0) return 0;
46         sum1=sum2=0; max1=max2=a[l]=a[r]=-inf;
47         int ll=0,rr=n+1;
48         for(int i=ll+1;i<=l;i++){
49             sum1+=a[i]; max1=max(max1,sum1);
50             if(sum1<=0) {pos1=i;break;}
51         }
52         for(int i=rr-1;i>=r;i--){
53             sum2+=a[i]; max2=max(max2,sum2);
54             if(sum2<=0) {pos2=i;break;}
55         }
56         while(1){
57             if(ll+1==l&&rr-1==r) return 1;
58             if(sum+max1<=0&&pos1!=l){
59                 sum+=sum1; sum1=0,ll=pos1; max1=-inf;
60                 for(int i=ll+1;i<=l;i++){
61                     sum1+=a[i]; max1=max(max1,sum1);
62                     if(sum1<=0) {pos1=i;break;}
63                 }
64             }
65             else if(sum+max2<=0&&pos2!=r){
66                 sum+=sum2; sum2=0,rr=pos2; max2=-inf;
67                 for(int i=rr-1;i>=r;i--){
68                     sum2+=a[i]; max2=max(max2,sum2);
69                     if(sum2<=0) {pos2=i;break;}
70                 }
71             }
72             else return 0;
73         }
74     }
75     inline short main(){
76         T=read();
77         while(T--){
78             n=read();k=read();memset(a,0,sizeof(a));
79             for(int i=0;i<n;i++) a[i]=read();
80             --n,--k; a[0]=a[n+1]=-inf; l=k+1,r=k;
81             puts(wsn(l,r)?"Yes":"No");
82         }
83         return 0;
84     }
85 }
86 signed main(){return WSN::main();}
View Code

 

T2 排列

还没改出来,快了,先沽了。。。。

不对,$50$分很好打,先打暴力全排,在找$k==1$的规律,直接$2^{n-1}$即可。

或许这就是我死刚$T2$的原因。接下来说一下考场思路:

$k==1$时,我以排列里最大值的位置作为列,最大值的值作为行,打出了一个杨辉三角。

//行是长度为n的排列里面的最大值,也就是n
//列是n的位置
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
。
。
。

不甘心一个"数学"题只拿$50$分的我决定打出$k==2$的同样原理的表,令我惊喜的是,他与杨辉三角有巨大关系

0
0 0
1 0 1
5 3 3 5
21 20 18 20 21
93 105 110 110 105 93
459 558 645 700 645 558 459
。
。
。
。
//不难发现每一项都是杨辉三角对应项的整倍数

于是我想要找到倍数能否按照组合数的原理推出关系。。。

于是

$\textit{2 hours later.......}$

$woc$,不行了,后面题还没动呢。。。跑了

就只有五十分了。。。

希望以后看到这篇博客的$OIer$不管是谁,想出这种东西的留下一个见解,我一定会看的

 

$UPD:2021.8.18$ 突然发现改出来了没填坑,来补坑了

$dp$数组表示很神,$sm_{i,j,0/1,0/1}$表示序列长度为$i$,操作了$j$次,序列里最大的那个值在左侧还是右侧的序列数

然后关于$dp$,方程式十分的形象,将一个序列以最大值为断点分开(枚举断点),那么分开的两个序列的$0/1$分别按照最大值的位置表示就行

然后剩下的数为了保证是一个排列,用组合数求方案$\binom{i-1}{k-1}$即可,处理出的是一个类似前缀和的东西,减一下就是答案

 1 #include<bits/stdc++.h>
 2 #define int long long
 3 using namespace std;
 4 inline int read(){
 5     int x=0,f=1; char ch=getchar();
 6     while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
 7     while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar();}
 8     return x*f;
 9 }
10 const int NN=10005;
11 int n,m,p,C[NN][NN],sm[NN][22][2][2];
12 namespace WSN{
13     inline short main(){
14         n=read(); m=read(); p=read();
15         for(register int i=0;i<=n;++i){
16             C[i][0]=1;
17             for(register int j=1;j<=i;++j)
18                 C[i][j]=(C[i-1][j-1]+C[i-1][j])%p;
19         }
20         for(register int i=0;i<=m+1;++i) sm[0][i][0][0]=sm[0][i][0][1]=sm[0][i][1][0]=sm[0][i][1][1]=1;
21         for(register int i=1;i<=n;++i) for(register int j=1;j<=m+1;++j) for(register int k=1;k<=i;++k){
22             (sm[i][j][0][0]+=sm[k-1][j][0][1]*sm[i-k][j][1][0]%p  *C[i-1][k-1])%=p;
23             (sm[i][j][0][1]+=sm[k-1][j][0][1]*sm[i-k][j-1][1][1]%p*C[i-1][k-1])%=p;
24             (sm[i][j][1][0]+=sm[k-1][j-1][1][1]*sm[i-k][j][1][0]%p*C[i-1][k-1])%=p;
25             (sm[i][j][1][1]+=(sm[k-1][j][1][1]*sm[i-k][j][1][1]-(sm[k-1][j][1][1]-sm[k-1][j-1][1][1])*(sm[i-k][j][1][1]-sm[i-k][j-1][1][1]))%p*C[i-1][k-1])%=p;
26         }
27         printf("%lld\n",((sm[n][m][0][0]-sm[n][m-1][0][0])%p+p)%p);
28         return 0;
29     }
30 }
31 signed main(){return WSN::main();}
View Code

 

T3 最短路

比较神仙的$dp$。

考虑从$n$到$1$的路径,一定是先往回走一段,再沿着$1$到$n$的路径走一段,再往回走一段,再沿着$1$到$n$的路径走一段

$zxs$:这样省钱。。。

确实!

我们设$f_{i,j}$表示向下走的边的,从$i-j$最小花费。$g_{i,j}$表示向上走的边的,从$i-j$最小花费。

 

 看着图大概理解一下,代码比较清楚,建边用$spfa$转移,注意代码里面将二维的$dp$变成了一维的,加个$n*n$判断是正向反向边

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 inline int read(){
 4     int x=0,f=1; char ch=getchar();
 5     while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }
 6     while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar();}
 7     return x*f;
 8 }
 9 const int NN=62505;
10 int n,m,w[255],dis[255][255],d[NN<<1];
11 bool g[255][255],vis[NN<<1];
12 vector< pair<int,int> > e[NN<<1];
13 int id(int x,int y){return (x-1)*n+y;}
14 inline void die_for_100(){
15     memset(dis,0x3f,sizeof(dis));
16     for(int i=1;i<=n;i++) dis[i][i]=0;
17     for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(g[i][j]) dis[i][j]=w[j];
18     for(int k=1;k<=n;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++)
19         dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
20 }
21 inline void hand_made_link(int x,int y,int z){e[x].push_back(make_pair(y,z));}
22 inline void spfa(){
23     queue<int> Q;
24     memset(d,0x3f,sizeof(d));
25     d[id(n,n)]=0; vis[id(n,n)]=1;
26     Q.push(id(n,n));
27     while(!Q.empty()){
28         int x=Q.front();Q.pop(); vis[x]=0;
29         for(int i=0;i<e[x].size();i++){
30             int y=e[x][i].first,z=e[x][i].second;
31             if(d[y]>d[x]+z){
32                 d[y]=d[x]+z;
33                 if(!vis[y]){
34                     Q.push(y);
35                     vis[y]=1;
36                 }
37             }
38         }
39     }
40 }
41 namespace WSN{
42     inline short main(){
43         n=read();m=read(); for(int i=1;i<=n;i++) w[i]=read();
44         for(int i=1;i<=m;i++) g[read()][read()]=1;
45         die_for_100();
46         for(int i=1;i<=n;i++)
47             for(int j=1;j<=n;j++)
48                 for(int k=1;k<=n;k++)
49                     if(dis[j][k]<1e9&&j!=k) hand_made_link(id(i,j),id(k,i)+n*n,dis[j][k]-w[k]);
50         for(int i=1;i<=n;i++)
51             for(int j=1;j<=n;j++)
52                 for(int k=1;k<=n;k++)
53                     if(dis[i][k]<1e9&&dis[k][j]<1e9) hand_made_link(id(i,j)+n*n,id(i,k),dis[i][k]+dis[k][j]);
54         spfa();
55         int ans=d[id(1,1)]+w[1];
56         if(ans<1e9) printf("%d\n",ans);
57         else printf("-1\n");
58         return 0;
59     }
60 }
61 signed main(){return WSN::main();}
View Code

 

T4 矩形

没改出来,菇沽姑

 

posted @ 2021-08-11 19:16  雪域亡魂  阅读(103)  评论(0编辑  收藏  举报