1. 程式人生 > 其它 >Noip模擬35 2021.8.10

Noip模擬35 2021.8.10

T1 玩遊戲 T2 排列 T3 最短路 T4 矩形

考試題目變成四道了,貌似確實根本改不完。。。

不過給了兩個小時頹廢時間確實很爽(蕪湖~~)

但是前幾天三道題改著不是很費勁的時候為什麼不給放鬆時間,

非要在改不完題的時候頹??

算了算了不碎碎唸了。。

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$不管是誰,想出這種東西的留下一個見解,我一定會看的

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 矩形

沒改出來,菇沽姑