【学习笔记】Public NOIP Round #3 简要题解

A.移除石子

模拟题。从左上角开始处理,讨论几种情况即可。

复杂度 O ( T n log ⁡ n ) O(Tn\log n) O(Tnlogn)

#include<bits/stdc++.h> #define ll long long #define pb push_back #define fi first #define se second using namespace std; int T,n,lsh[3005],X[3005],Y[3005],cnt; deque<int>v[3005]; int get(int x){return lower_bound(lsh+1,lsh+1+cnt,x)-lsh;} void print(pair<int,int>x,pair<int,int>y){ pair<int,int>le=make_pair(max(x.fi,y.fi),max(x.se,y.se));int d=max(abs(x.fi-y.fi),abs(x.se-y.se)); cout<<le.fi-d<<' '<<le.se-d<<' '<<le.fi<<' '<<le.se<<"\n"; } int main(){ freopen("stone.in","r",stdin); freopen("stone.out","w",stdout); ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>T; while(T--){ cin>>n,cnt=0;for(int i=1;i<=n;i++)cin>>X[i]>>Y[i],lsh[++cnt]=X[i]; sort(lsh+1,lsh+1+cnt),cnt=unique(lsh+1,lsh+1+cnt)-lsh-1; for(int i=1;i<=cnt;i++)v[i].clear();for(int i=1;i<=n;i++)v[get(X[i])].push_back(Y[i]); cout<<"Yes"<<"\n"; pair<int,int>fuck=make_pair(0x3f3f3f3f,0x3f3f3f3f); for(int i=1;i<=cnt;i++){ sort(v[i].begin(),v[i].end()); while(v[i].size()>1){ int x=v[i][0];v[i].pop_front(); int y=v[i][0];v[i].pop_front(); if(y<fuck.se){ int d=y-x; cout<<lsh[i]-d<<' '<<x<<' '<<lsh[i]<<' '<<y<<"\n"; } else if(y>fuck.se){ print(fuck,make_pair(lsh[i],x)),fuck=make_pair(0x3f3f3f3f,0x3f3f3f3f),v[i].push_front(y); } else { if(lsh[i]-fuck.fi>y-x)print(make_pair(lsh[i],x),make_pair(lsh[i],y)); else if(lsh[i]-fuck.fi<y-x)print(fuck,make_pair(lsh[i],y)),v[i].push_front(x); else { cout<<fuck.fi<<".5"<<' '<<x<<' '<<lsh[i]<<".5"<<' '<<y<<"\n"; } } } if(v[i].size()){ if(fuck.fi==0x3f3f3f3f)fuck=make_pair(lsh[i],v[i][0]); else { print(fuck,make_pair(lsh[i],v[i][0])),fuck=make_pair(0x3f3f3f3f,0x3f3f3f3f); } } } } }

B.抓内鬼

这题没想到抽屉原理纯属脑抽

首先把最短路图建出来,如果 1 → n 1\to n 1n直接相连的话直接把 { 1 , n } \{1,n\} {1,n}放到同一组即可。

否则我们假设不存在长度为 3 3 3的最短路。设直接与 1 1 1相连的点的集合为 S S S,与 n n n相连的点的集合为 T T T,显然 S ∩ T = ∅ S\cap T=\empty ST=,大概是这个样子:

请添加图片描述
显然只需把 S S S或者 T T T全部分到同一组即可。而根据抽屉原理这是显然成立的。

对于长度为 3 3 3的最短路,由于 1 , n 1,n 1,n分在了不同组,所以中间这个点总会与 1 1 1, n n n中的一个分在同一组。这样从 S S S T T T的任何路径都存在一条边满足两个端点被分在了同一组。

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

#include<bits/stdc++.h> #define ll long long #define pb push_back #define fi first #define se second using namespace std; int n,m,K,vis[200005],vis2[200005],a[200005],dis[200005],res[200005]; vector<int>g[200005],vec; priority_queue<pair<int,int>>q; struct node{ int u,v; }e[200005]; int main(){ freopen("catch.in","r",stdin); freopen("catch.out","w",stdout); ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n>>m>>K,memset(res,-1,sizeof res); int l=K,r=n-K; if(K==0){for(int i=1;i<=n;i++)cout<<"P";return 0;} if(K==n){for(int i=1;i<=n;i++)cout<<"U";return 0;} for(int i=1;i<=n;i++)cin>>a[i]; for(int i=1;i<=m;i++){ int u,v;cin>>u>>v,g[u].pb(v),g[v].pb(u),e[i]={u,v}; } memset(dis,0x3f,sizeof dis),dis[1]=0,q.push(make_pair(0,1)); while(q.size()){ int u=q.top().se;q.pop(); if(vis[u])continue;vis[u]=1; for(auto v:g[u]){ if(dis[u]+a[v]<dis[v])dis[v]=dis[u]+a[v],q.push(make_pair(-dis[v],v)); } }for(auto u:g[n]){ if(dis[u]+a[n]==dis[n]){ vis2[u]=1,vec.pb(u); } }vec.pb(n); if(vis2[1]){ if(l>=2)res[1]=res[n]=0,l-=2; else if(r>=2)res[1]=res[n]=1,r-=2; else { cout<<"impossible"; return 0; } } else { if(l>=vec.size()){ for(auto x:vec)res[x]=0,l--; res[1]=1,r--; } else if(r>=vec.size()){ for(auto x:vec)res[x]=1,r--; res[1]=0,l--; } else if(l>=n-vec.size()){ for(int i=1;i<n;i++){ if(!vis2[i])res[i]=0,l--; }res[n]=1,r--; } else { for(int i=1;i<n;i++){ if(!vis2[i])res[i]=1,r--; }res[n]=0,l--; } } for(int i=1;i<=n;i++){ if(res[i]==-1){ if(l)res[i]=0,l--;else res[i]=1,r--; }cout<<(res[i]?"P":"U"); } }

C.异或序列

水题,但是我没看出来

考虑最朴素的状态定义 ,设 d p i dp_i dpi表示以 i i i结尾的合法序列数目。那么 d p i = ∑ j < i d p j − ∑ j < i , p ( i ) = p ( j ) d p i ⊕ j dp_{i}=\sum_{j<i}dp_j-\sum_{j<i,p(i)=p(j)}dp_{i\oplus j} dpi=j<idpjj<i,p(i)=p(j)dpij 。其中 p ( i ) p(i) p(i)表示二进制下最高位的 1 1 1

不难看出后面的式子可以前缀和优化,时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

#include<bits/stdc++.h> #define ll long long #define pb push_back #define fi first #define se second using namespace std; int n,mod; ll res,dp[1000005],sum[1000005]; int main(){ freopen("xor.in","r",stdin); freopen("xor.out","w",stdout); ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n>>mod,dp[1]=sum[1]=res=1; for(int i=2;i<=n;i++){ dp[i]=sum[i-1]+1; int j=29;while(!(i>>j&1))j--; for(int k=j-1;k>=0;k--){ if(i>>k&1){ dp[i]=(dp[i]-sum[(1<<(k+1))-1]+sum[(1<<k)-1])%mod; } }sum[i]=(sum[i-1]+dp[i])%mod; }cout<<(sum[n]+mod)%mod; }

D.数圈圈

分治好题。之前没遇到过这个想法。

考虑对矩阵分治,每次选择长的一边割开,然后计算跨过 mid \text{mid} mid的圈的数目。

在这里插入图片描述
枚举 u u u, v v v,只需将左右方案数相乘。

l u l_u lu表示向左延伸的最大长度, d i , u d_{i,u} di,u表示第 i i i列向下延伸的最大长度。

要求 ∑ i = max ⁡ ( l u , l v ) m i d [ d i , u ≥ v ] \sum_{i=\max(l_u,l_v)}^{mid}[d_{i,u}\ge v] i=max(lu,lv)mid[di,uv]。不妨假设 l u > l v l_u>l_v lu>lv(后者再做一遍即可),原式变成了 ∑ i = l u m i d [ d i , u ≥ v ] \sum_{i=l_u}^{mid}[d_{i,u}\ge v] i=lumid[di,uv],只有 v v v一个变量,用桶维护即可。

单层复杂度 O ( x y + x 2 ) = O ( x y ) O(xy+x^2)=O(xy) O(xy+x2)=O(xy) x ≤ y x\le y xy),即矩形面积。每一层复杂度 O ( n m ) O(nm) O(nm),总复杂度 O ( n m ( log ⁡ n + log ⁡ m ) ) O(nm(\log n+\log m)) O(nm(logn+logm))

或许,这就是oi的奇妙之处吧

#include<bits/stdc++.h> #define ll long long #define pb push_back #define fi first #define se second using namespace std; int n,m; int up[2005][2005],down[2005][2005],le[2005][2005],ri[2005][2005],siz[2005][2005],siz2[2005][2005],tong[2005]; char s[2005][2005]; ll solve(int X,int X2,int Y,int Y2){ if(X==X2||Y==Y2)return 0; ll res=0; if(X2-X>Y2-Y){ int mid=X+X2>>1; res+=solve(X,mid,Y,Y2)+solve(mid+1,X2,Y,Y2); for(int i=Y;i<=Y2;i++)for(int j=Y;j<=Y2;j++)siz[i][j]=siz2[i][j]=0; for(int i=Y;i<=Y2;i++){ int l=max(X,up[mid][i]),tot=0; for(int j=i+1;j<=Y2;j++)tong[j]=0; for(int j=l;j<=mid;j++)tong[min(Y2,ri[j][i])]++; for(int j=Y2;j>i;j--){ tot+=tong[j];if(max(X,up[mid][j])<=l)siz[i][j]+=tot; } } for(int i=Y2;i>=Y;i--){ int l=max(X,up[mid][i]),tot=0; for(int j=i-1;j>=Y;j--)tong[j]=0; for(int j=l;j<=mid;j++)tong[max(Y,le[j][i])]++; for(int j=Y;j<i;j++){ tot+=tong[j];if(max(X,up[mid][j])<l)siz[j][i]+=tot; } } for(int i=Y;i<=Y2;i++){ int l=min(X2,down[mid+1][i]),tot=0; for(int j=i+1;j<=Y2;j++)tong[j]=0; for(int j=mid+1;j<=l;j++)tong[min(Y2,ri[j][i])]++; for(int j=Y2;j>i;j--){ tot+=tong[j];if(min(X2,down[mid+1][j])>=l)siz2[i][j]+=tot; } } for(int i=Y2;i>=Y;i--){ int l=min(X2,down[mid+1][i]),tot=0; for(int j=i-1;j>=Y;j--)tong[j]=0; for(int j=mid+1;j<=l;j++)tong[max(Y,le[j][i])]++; for(int j=Y;j<i;j++){ tot+=tong[j];if(min(X2,down[mid+1][j])>l)siz2[j][i]+=tot; } } for(int i=Y;i<=Y2;i++)for(int j=i+1;j<=Y2;j++)if(s[mid][i]==s[mid+1][i]&&s[mid][j]==s[mid+1][j])res+=(ll)siz[i][j]*siz2[i][j]; } else { int mid=Y+Y2>>1; res+=solve(X,X2,Y,mid)+solve(X,X2,mid+1,Y2); for(int i=X;i<=X2;i++)for(int j=X;j<=X2;j++)siz[i][j]=siz2[i][j]=0; for(int i=X;i<=X2;i++){ int l=max(Y,le[i][mid]),tot=0; for(int j=i+1;j<=X2;j++)tong[j]=0; for(int j=l;j<=mid;j++)tong[min(X2,down[i][j])]++; for(int j=X2;j>i;j--){ tot+=tong[j];if(max(Y,le[j][mid])<=l)siz[i][j]+=tot; } } for(int i=X2;i>=X;i--){ int l=max(Y,le[i][mid]),tot=0; for(int j=i-1;j>=X;j--)tong[j]=0; for(int j=l;j<=mid;j++)tong[max(X,up[i][j])]++; for(int j=X;j<i;j++){ tot+=tong[j];if(max(Y,le[j][mid])<l)siz[j][i]+=tot; } } for(int i=X;i<=X2;i++){ int l=min(Y2,ri[i][mid+1]),tot=0; for(int j=i+1;j<=X2;j++)tong[j]=0; for(int j=mid+1;j<=l;j++)tong[min(X2,down[i][j])]++; for(int j=X2;j>i;j--){ tot+=tong[j];if(min(Y2,ri[j][mid+1])>=l)siz2[i][j]+=tot; } } for(int i=X2;i>=X;i--){ int l=min(Y2,ri[i][mid+1]),tot=0; for(int j=i-1;j>=X;j--)tong[j]=0; for(int j=mid+1;j<=l;j++)tong[max(X,up[i][j])]++; for(int j=X;j<i;j++){ tot+=tong[j];if(min(Y2,ri[j][mid+1])>l)siz2[j][i]+=tot; } } for(int i=X;i<=X2;i++)for(int j=i+1;j<=X2;j++)if(s[i][mid]==s[i][mid+1]&&s[j][mid]==s[j][mid+1])res+=(ll)siz[i][j]*siz2[i][j]; } return res; } int main(){ freopen("circle.in","r",stdin); freopen("circle.out","w",stdout); cin>>n>>m;for(int i=1;i<=n;i++)scanf("%s",s[i]+1); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ up[i][j]=(s[i-1][j]==s[i][j])?up[i-1][j]:i; le[i][j]=(s[i][j-1]==s[i][j])?le[i][j-1]:j; } } for(int i=n;i>=1;i--){ for(int j=m;j>=1;j--){ down[i][j]=(s[i+1][j]==s[i][j])?down[i+1][j]:i; ri[i][j]=(s[i][j+1]==s[i][j])?ri[i][j+1]:j; } } cout<<solve(1,n,1,m); }

总结:这次考试完全不在状态, T 2 T2 T2, T 3 T3 T3都没有观察出来, T 4 T4 T4也反映出知识存在漏洞,看来应该尽快从 csp \text{csp} csp中清醒过来,踏实做题,积累思维。


__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530091.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(19)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示