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 namespaceView Codestd; 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();}
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 矩形
沒改出來,菇沽姑