noip多校模拟25
考试过程:这次考试,觉得挺奇怪的,会的题一眼就会,不会的怎么想都不会。
按顺序开题,首先是T1,看到数据范围,觉得应该是\(o(n)\)出答案。然后基本上就是一眼出答案了。然后去厕所蹲了一会捋了捋思路,回来就切了。然后是T2,就很奇怪,怎么想也不会,就先弃了。T3,还是一眼出答案,我们可以枚举起点和循环节的长度,复杂度应该是\(o(n^2\times log)\)的,但是考场上有个细节打错了,因为当时没有仔细算复杂度,就疯狂卡常,结果挂了\(40pts\).最后是T4,还是一眼出做法,最后优化一下就切了。
T1 石子合并
思路:显然,如果同时存在正数和负数,那么所有负数都会造成正贡献。那么我们对于只存在正数和负数的情况只需要拿出最大值减去最小值即可。
代码如下:
AC_code
#include<bits/stdc++.h>
#define int long long
#define re register int
#define ii inline int
#define iv inline void
using namespace std;
const int N=2e6+10;
const int INF=1e18;
int t,n,ja,jb,mina,maxa,minb,maxb;
int a[N],b[N];
ii read()
{
int x=0; char ch=getchar(); bool f=1;
while(ch<'0' or ch>'9')
{
if(ch=='-') f=0;
ch=getchar();
}
while(ch>='0' and ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return f?x:(-x);
}
#undef int
int main()
{
freopen("stone.in","r",stdin);
freopen("stone.out","w",stdout);
#define int long long
t=read();
while(t--)
{
int x;
ja=jb=0;
maxa=maxb=-INF;
mina=minb=INF;
n=read();
for(re i=1;i<=n;i++)
{
x=read();
if(x>0)
a[++ja]=x,mina=min(mina,x),maxa=max(maxa,x);
else if(x<0)
b[++jb]=x,minb=min(minb,x),maxb=max(maxb,x);
}
if(ja and jb)
{
int now=0;
for(re i=1;i<=jb;i++) now=now+abs(b[i]);
for(re i=1;i<=ja;i++) now=now+a[i];
printf("%lld\n",now);
}
else if(ja)
{
if(ja==1) printf("%lld\n",a[1]);
else if(ja==2) printf("%lld\n",abs(a[1]-a[2]));
else
{
bool hn=0,hx=0;
int now=maxa-mina;
for(re i=1;i<=ja;i++)
{
if(a[i]==mina and hn==0) {hn=1;continue;}
if(a[i]==maxa and hx==0) {hx=1;continue;}
now=now+a[i];
}
printf("%lld\n",now);
}
}
else if(jb)
{
if(jb==1) printf("%lld\n",b[1]);
else if(jb==2) printf("%lld\n",abs(b[1]-b[2]));
else
{
bool hn=0,hx=0;
int now=maxb-minb;
for(re i=1;i<=jb;i++)
{
if(b[i]==minb and hn==0) {hn=1;continue;}
if(b[i]==maxb and hx==0) {hx=1;continue;}
now=now-b[i];
}
printf("%lld\n",now);
}
}
else printf("0\n");
}
return 0;
}
T2 翻转游戏
思路:
最后去重即可。
代码如下:
AC_code
#include<bits/stdc++.h>
#define int long long
#define re register int
#define ii inline int
#define iv inline void
using namespace std;
const int N=3e5+10;
const int INF=1e9+10;
struct node
{
int x,y,x2,y2;
}cun[N],pre[N],suf[N];
int t,p,q,n,ans,base;
ii read()
{
int x=0; char ch=getchar(); bool f=1;
while(ch<'0' or ch>'9')
{
if(ch=='-') f=0;
ch=getchar();
}
while(ch>='0' and ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return f?x:(-x);
}
iv init()
{
for(re i=0;i<=n+1;i++) pre[i]=suf[i]=(node){0,0,INF,INF};
ans=base=0;
}
signed main()
{
freopen("carpet.in","r",stdin);
freopen("carpet.out","w",stdout);
t=read();
while(t--)
{
p=read(),q=read(),n=read();
init();
for(re i=1;i<=n;i++) cun[i]=(node){read(),read(),read()-1,read()-1};
pre[1]=(node){cun[1].x,cun[1].y,cun[1].x2,cun[1].y2};
suf[n]=(node){cun[n].x,cun[n].y,cun[n].x2,cun[n].y2};
for(re i=2;i<=n;i++)
{
pre[i].x=max(pre[i-1].x,cun[i].x);
pre[i].y=max(pre[i-1].y,cun[i].y);
pre[i].x2=min(pre[i-1].x2,cun[i].x2);
pre[i].y2=min(pre[i-1].y2,cun[i].y2);
}
for(re i=n-1;i;i--)
{
suf[i].x=max(suf[i+1].x,cun[i].x);
suf[i].y=max(suf[i+1].y,cun[i].y);
suf[i].x2=min(suf[i+1].x2,cun[i].x2);
suf[i].y2=min(suf[i+1].y2,cun[i].y2);
}
if(pre[n].x2>=pre[n].x and pre[n].y2>=pre[n].y) ans=base=(pre[n].x2-pre[n].x+1)*(pre[n].y2-pre[n].y+1);
for(re i=1;i<=n;i++)
{
int x=max(pre[i-1].x,suf[i+1].x);
int y=max(pre[i-1].y,suf[i+1].y);
int x2=min(pre[i-1].x2,suf[i+1].x2);
int y2=min(pre[i-1].y2,suf[i+1].y2);
if(x2>=x and y2>=y)
{
ans+=(x2-x+1)*(y2-y+1);
if(pre[n].x>=x and pre[n].x2<=x2 and pre[n].y>=y and pre[n].y2<=y2) ans-=base;
}
}
printf("%lld\n",ans);
}
return 0;
}
T3 优美的旋律
思路:我们可以考虑枚举起点和循环节长度,然后计算循环节最长的循环次数,可以证明,这样的复杂度应该在\(O(n^2\times log)\)左右。
代码如下:
AC_code
#include<bits/stdc++.h>
#define re register int
#define ii inline int
#define iv inline void
using namespace std;
typedef unsigned long long ull;
const int N=3100;
long long a,b,len;
long long ans;
char s[N];
ull base=1331;
ull ch[N],hs[N];
ii read()
{
int x=0; char ch=getchar(); bool f=1;
while(ch<'0' or ch>'9')
{
if(ch=='-') f=0;
ch=getchar();
}
while(ch>='0' and ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return f?x:(-x);
}
int main()
{
freopen("melody.in","r",stdin);
freopen("melody.out","w",stdout);
a=read(),b=read();
scanf("%s",s+1);
len=strlen(s+1);
ch[0]=1;
for(re i=1;i<=len+5;i++) ch[i]=ch[i-1]*base;
for(re i=1;i<=len;i++) hs[i]=hs[i-1]*base+(ull)s[i];
for(re i=1;i<=len;i++)
{
int dis=len-i+1,mx=(dis/2);
for(re j=1;j<=mx;j++)
{
ull my=hs[i+j-1]-hs[i-1]*ch[j];
int up=len-j+1;
long long now=0;
for(re k=i+j;k<=up;k+=j)
{
if( my != (hs[k+j-1]-hs[k-1]*ch[j]) ) break;
else ++now;
}
if(now) ++now,ans=max(ans,a*j+b*now);
}
}
printf("%lld\n",ans);
return 0;
}
T4 基站建设
思路:因为要找两个主站和两个副站,那么我们可以枚举两个点作为主站,再枚举两个主站里儿子数较少的那个,计算最大值即可。
代码如下:
AC_code
#include<bits/stdc++.h>
#define ll long long
#define re register int
#define ii inline int
#define iv inline void
using namespace std;
const int N=5e4+10;
int n,m;
ll ans;
ll val[N];
vector<int> v[N];
bitset<N> BS[N];
ii read()
{
int x=0; char ch=getchar(); bool f=1;
while(ch<'0' or ch>'9')
{
if(ch=='-') f=0;
ch=getchar();
}
while(ch>='0' and ch<='9')
{
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return f?x:(-x);
}
inline bool com(int x,int y) {return val[x]<val[y];}
int main()
{
freopen("station.in","r",stdin);
freopen("station.out","w",stdout);
n=read(),m=read();
for(re i=1;i<=n;i++) val[i]=read();
int x,y;
for(re i=1;i<=m;i++)
{
x=read(),y=read();
v[x].push_back(y),v[y].push_back(x);
BS[x][y]=1,BS[y][x]=1;
}
for(re i=1;i<=n;i++) if(v[i].size()) sort(v[i].begin(),v[i].end(),com);
for(re i=1;i<=n;i++)
{
for(re j=0;j<v[i].size();j++)
{
int p=v[i][j];
bitset<N> tmp=BS[i]&BS[p];
if(tmp.count()<2) continue;
ll mx1=0,mx2=0,pos=0;
if(v[i].size()<=v[p].size())
{
for(re k=v[i].size()-1;k>=0;k--)
{
if(mx1 and mx2) break;
if(BS[p][v[i][k]])
{
if(!mx1) mx1=val[v[i][k]];
else if(!mx2) mx2=val[v[i][k]];
}
}
}
else
{
for(re k=v[p].size()-1;k>=0;k--)
{
if(mx1 and mx2) break;
if(BS[i][v[p][k]])
{
if(!mx1) mx1=val[v[p][k]];
else if(!mx2) mx1=val[v[p][k]];
}
}
}
ans=max(ans,(val[i]+1)*(val[p]+1)+mx1*mx2);
}
}
printf("%lld\n",ans);
return 0;
}