2021.11.17 NOIP模拟
题很好,只不过咱不会做。
T1

模拟即可。
#include<cstdio>
//#define zczc
const int N=600010;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar(); }
wh*=f;return;
}
inline bool get(){
char w=getchar();
while(w!='0'&&w!='1')w=getchar();
return w=='1';
}
inline void check(int &s1,int s2){if(s1<s2)s1=s2;return;}
int m,ans,l[N],r[N];
bool a[N];
signed main(){
#ifdef zczc
freopen("in.txt","r",stdin);
#endif
#ifndef zczc
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
read(m);
for(int i=1;i<=m;i++)a[i]=get();
for(int i=1;i<=m;i++)l[i]=a[i]==a[i-1]?l[i-1]:i-1;
for(int i=m;i;i--)r[i]=(a[i]==a[i+1]&&i!=m)?r[i+1]:i+1;
for(int i=1;i<=m;i++)check(ans,r[r[i]]-l[l[i]]-r[i]+l[i]),check(ans,r[i]-l[i]-1);
printf("%d",ans);
return 0;
}
T2


- 解法
可以想到用 \(f[i][j]\) 表示以 \(a_i\) 和 \(a_j\) 结尾的合法序列的最大长度,然后更新即可。主要问题是在后面那么多数中找到一个恰好等于 \(a_i+a_j\) 的数,哈希即可,虽然我卡了半天常又吸了半天氧才过,后来看题解发现似乎对于每个数只需要更新它后面第一个符合条件的数即可,因为这样肯定可以保证最优……
太弱了。
#include<cstdio>
#include<vector>
#define abs(wh) (wh<0?wh+inf:wh)
//#define zczc
using namespace std;
const int N=3010;
const int M=100011;
const int inf=1e9+7;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar(); }
wh*=f;return;
}
inline void check(int &s1,int s2){
if(s1<s2)s1=s2;return;
}
int num[N],cnt;
struct node{
int val,pl,next,beg;
}h[M];
int hsum,head[M];
inline void push(node wh){
int pl=abs(wh.val)%M;
hsum++;
h[hsum]=wh;
h[hsum].next=head[pl];
h[head[pl]].beg=hsum;
head[pl]=hsum;
return;
}
void find(int wh,int val){
int pl=abs(val)%M;cnt=0;
for(int i=head[pl];i;i=h[i].next){
if(h[i].val==val){
if(h[i].pl>wh)num[++cnt]=h[i].pl;
else h[h[i].beg].next=h[i].next;
}
}
return;
}
int m,a[N],f[N][N],ans;
signed main(){
#ifdef zczc
freopen("in.txt","r",stdin);
#endif
#ifndef zczc
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
#endif
read(m);
for(int i=1;i<=m;i++){
read(a[i]);
push((node){a[i],i,0});
}
for(int i=2;i<=m;i++){
for(int j=1;j<i;j++){
check(f[i][j],2);
check(ans,f[i][j]);
find(i,a[i]+a[j]);
for(int k=1;k<=cnt;k++)check(f[num[k]][i],f[i][j]+1);
}
}
printf("%d",ans);
return 0;
}
T3

很有趣的一道……贪心题?
首先要理清楚一个概念,就是它那里的所谓a-中位数。它定义的是\(w_{(len-a-1)/2}\),len为序列长度。经过简单的模拟发现a-中位数等同于把序列排序之后去掉最大的a个数之后的中位数,这一点很重要。
然后就发现可以转变思路,从求a为某个值时答案是多少到每个数为x-中位数时x的取值范围。
考虑假如y作为了x-中位数,那么就可以知道这棵树上一定存在一个连通块,其中有一半的数大于y,另一半小于y。如果考虑把所有元素1和-1化(大的为1,小的为0),问题就变成了是否存在一个连通块使得所有元素的权值之和为0。另外还有一个结论就是,假如可以找到一个连通块的权值之和大于0,那么一定会存在一个连通块等于0,因为只需在前者的基础上删去一些权值为1的数即可。
说到这里应该就没什么了吧。
#include<cstdio>
//#define zczc
const int N=200010;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}
wh*=f;return;
}
inline int max(int s1,int s2){
return s1<s2?s2:s1;
}
int m,a[N],pl[N],f[N<<1],g[N<<1],an[N];
inline void work(int wh){
int lc=wh<<1,rc=wh<<1|1;
//solve f
f[wh]=max(a[wh]+f[lc]+f[rc],0);
//solve g
g[wh]=max(max(g[lc],g[rc]),f[wh]);
return;
}
signed main(){
#ifdef zczc
freopen("in.txt","r",stdin);
#endif
read(m);
for(int i=1;i<=m;i++){
read(a[i]);pl[a[i]]=i;a[i]=1;
}
for(int i=m;i;i--)f[i]=g[i]=f[i<<1]+f[i<<1|1]+1;
for(int i=1;i<=m;i++){
if(i^1){
a[pl[i-1]]=-1;
int now=pl[i-1];
while(now){
work(now);
now>>=1;
}
}
an[g[1]-1]=i;
}
for(int i=m-1;i>=0;i--)an[i]=max(an[i+1],an[i]);
for(int i=0;i<m;i++)printf("%d ",an[i]);
return 0;
}
T4
很有趣的一道卡常题,专门讲一下。
一如既往,万事胜意

浙公网安备 33010602011771号