初三奥赛模拟测试4
前言
\(CSP-S\) 模拟赛,确实比前几次简单多了。
-
\(T1~100pts\) : 签到题。
-
\(T2~100pts\) :二分直接跑即可。
-
\(T3~40pts\) :
首先他给的这个快读没法用。
赛时
joker
了,打了个 \(n^2~DP\) ,然后优化了 \(50min\) 换了个转移方程还是 \(n^2\) ,复杂度打假了。因为赛时懒得跑线段树,而且知道这玩意常数太大过不去,赛后听有人说 \(ST\) 表才恍然大悟,成
joker
了 。 -
\(T4~0pts\) :光顾着调 \(T3\) 了,没怎么看。
-
比赛链接 。
\(T1\) 最后一课
签到题,没什么好说的,将军饮马问题 。
点击查看代码
#include<bits/stdc++.h>
#define int __int128
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=2e5+10;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=true;
register char c=getchar();
for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
x=(z?x:~x+1);
}
void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');}
void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);}
int k,x,y,xx,yy;
signed main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
read(k),read(x),read(y),read(xx),read(yy);
if((y<=k&&yy<=k)||(y>=k&&yy>=k))
{
y=k*2-y;
write((xx-x)*(xx-x)+(yy-y)*(yy-y));
return 0;
}
write((xx-x)*(xx-x)+(yy-y)*(yy-y));
}
\(T2\) 日常
二分直接跑即可,记得排序。
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=1e5+10;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=true;
register char c=getchar();
for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
x=(z?x:~x+1);
}
void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');}
void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);}
int n,m,k;
bool v[N];
struct aa
{
int l,r,x,y;
}e[N];
bool cmp(aa a,aa b) {return a.l==b.l?a.r<b.r:a.l<b.l;}
int ask1(int i)
{
int l=1,r=n,mid,ans=0;
while(l<=r)
{
mid=(l+r)>>1;
if(e[mid].l>i) r=mid-1;
if(e[mid].r<i) l=mid+1;
if(e[mid].l<=i&&e[mid].r>=i)
{
ans=mid;
break;
}
}
return ans;
}
int ask2(int i)
{
int l=1,r=n,mid,ans=0;
while(l<=r)
{
mid=(l+r)>>1;
if(e[mid].x>i) r=mid-1;
if(e[mid].y<i) l=mid+1;
if(e[mid].x<=i&&e[mid].y>=i)
{
ans=mid;
break;
}
}
return ans;
}
signed main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
read(n),read(m),read(k);
for(int i=1,l,r,x,y;i<=n;i++)
{
read(l),read(x),read(y),read(r);
e[i]={l,r,x,y};
}
sort(e+1,e+1+n,cmp);
for(int i;k>=1;k--)
{
read(i);
int ans=ask1(i),anss=ask2(i);
if(ans==0) puts("Failed");
else if(v[ans]) puts("Again");
else if(anss) puts("Perfect");
else puts("Normal");
v[ans]=1;
}
}
\(T3\) 渡尘
部分分
-
\(20pts\) :
纯暴力跑,\(O(n^2m)\) 。
-
\(40pts\) :
\(O(n^2)\) 区间 \(DP\) 。
转移方程:
\[f_{l,r}=\max\{f_{l,r},abs(sum_r-sum_{l-1}),f_{l+1,r},f_{l,r-1}\} \]点击查看代码
#include<bits/stdc++.h> #define int long long #define endl '\n' #define sort stable_sort using namespace std; const int N=5010; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=true; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');} void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);} int n,m,a[N],sum[N],f[N][N]; signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif read(n),read(m); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) f[i][j]=-0x3f3f3f3f; for(int i=1;i<=n;i++) read(a[i]), sum[i]=sum[i-1]+a[i], f[i][i]=abs(a[i]); for(int i=1;i<=n;i++) for(int j=i-1;j>=1;j--) f[i][j]=max({ f[i][j], abs(sum[i]-sum[j-1]), abs(sum[i]-sum[j]), abs(sum[i-1]-sum[j-1]), f[i][j+1],f[i-1][j],f[i-1][j+1] }); for(int l,r;m>=1;m--) read(l),read(r), write(f[r][l]),puts(""); }
-
\(70pts\) :
线段树维护区间最大子段和,类似 山海经 这道题。
记录每个区间的最大前缀和,最大后缀和,最大字段和,区间和。
-
最大字段和 \(=\) 左区间最大后缀 \(+\) 右区间最大前缀。
-
最大前缀和 \(=\max\{\) 左区间最大前缀和,左区间和 \(+\) 右区间最大前缀和 \(\}\) 。最大后缀和同理。
复杂度 \(O(m\log(n))\) ,理论可过,但由于线段树的超级常数,只能拿 \(70pts\) 。
代码没打,沾 \(Hangry\) 的了。点击查看代码
#include <bits/stdc++.h> #define int long long #define lson (id << 1) #define rson (id << 1 | 1) using namespace std ; const int N = 2e5 + 100 ; inline int read() { int x = 0 , f = 1 ; char c = getchar() ; while ( c < '0' || c > '9' ) { if ( c == '-' ) f = -f ; c = getchar() ; } while ( c >= '0' && c <= '9' ) { x = x * 10 + c - '0' ; c = getchar() ; } return x * f ; } int n , m ; int a[N] ; class AVST { public: int Max , sum ; int left_min , right_min ; int left_max , right_max ; } t[N << 2] ; inline unsigned int Absolute ( int x ) { return x >= 0 ? x : -x ; } inline int max( int a , int b ) { return a > b ? a : b ; } inline int min( int a , int b ) { return a < b ? a : b ; } inline void push_up( AVST &Main , AVST Lson , AVST Rson ) { Main.sum = Lson.sum + Rson.sum ; Main.left_max = max( Lson.left_max , Lson.sum + Rson.left_max ) ; Main.left_min = min( Lson.left_min , Lson.sum + Rson.left_min ) ; Main.right_max = max( Rson.right_max , Rson.sum + Lson.right_max ) ; Main.right_min = min( Rson.right_min , Rson.sum + Lson.right_min ) ; Main.Max = max( max( Main.sum , max( Lson.Max , Rson.Max ) ) , max( Absolute( Lson.right_max + Rson.left_max ) , Absolute( Lson.right_min + Rson.left_min ) ) ) ; } void build( int id , int l , int r ) { if ( l == r ) { t[id].Max = Absolute( a[l] ) ; t[id].sum = t[id].left_max = t[id].left_min = t[id].right_max = t[id].right_min = a[l] ; return ; } int mid = ( l + r ) >> 1 ; build( lson , l , mid ) ; build( rson , mid + 1 , r ) ; push_up( t[id] , t[lson] , t[rson] ) ; } AVST Query( int id , int l , int r , int x , int y ) { if ( x <= l && r <= y ) { return t[id] ; } int mid = ( l + r ) >> 1 ; if ( x <= mid && mid < y ) { AVST reso = {0 , 0 , 0 , 0 , 0 , 0} , lef , rig ; lef = Query( lson , l , mid , x , y ) ; rig = Query( rson , mid + 1 , r , x , y ) ; push_up( reso , lef , rig ) ; return reso ; } else { if ( x <= mid ) return Query( lson , l , mid , x , y ) ; if ( y >= mid + 1 ) return Query( rson , mid + 1 , r , x , y ) ; AVST bre ; return bre ; } } signed main() { #ifndef ONLINE_JUDGE freopen( "1.in" , "r" , stdin ) ; freopen( "1.out", "w" ,stdout ) ; #endif n = read() ; m = read() ; for ( int i = 1 ; i <= n ; ++ i ) { a[i] = read() ; } build( 1 , 1 , n ) ; int x , y ; for ( int i = 1 ; i <= m ; ++ i ) { x = read() , y = read() ; if ( i == m ) printf( "%lld" , Query( 1 , 1 , n , x , y ).Max ) ; else printf( "%lld\n" , Query( 1 , 1 , n , x , y ).Max ) ; } }
-
-
\(100pts\) :
因为没有修改,用猫树维护,\(O(n\log(n)+m)\) ,但是我不会。
正解
用 \(sum_i\) 表示前缀和。
则区间 \([l\sim r]\) 和的绝对值就是 \(\max(sum_r-sum_{l-1},sum_{l-1}-sum_r)\) 。
那么问题就转化为在区间 \([l-1\sim r]\) 中找一个最大的 \(sum_x\) 和一个最小的 \(sum_y\) ,\(sum_x-sum_y\) 即为所求。
直接用 \(ST\) 表维护即可,复杂度 \(O(n\log(n)+m)\) 。
注意预处理时循环从 \(0\) 开始,因为 \(l-1\) 可能 \(=0\) 。
这个正解甚至比那些歪解简单得多。
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=2e5+10;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=true;
register char c=getchar();
for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
x=(z?x:~x+1);
}
void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');}
void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);}
int n,m,a[N],sum[N],mx[N][30],mi[N][30];
void RMQ()
{
for(int i=0;i<=n;i++)
for(int j=0;j<=log2(n);j++)
mi[i][0]=mx[i][0]=-0x3f3f3f3f;
for(int i=0;i<=n;i++)
mx[i][0]=sum[i],
mi[i][0]=-sum[i];
for(int j=1;j<=log2(n);j++)
for(int i=0;i<=n-(1<<j)+1;i++)
mx[i][j]=max(mx[i][j-1],mx[i+(1<<(j-1))][j-1]),
mi[i][j]=max(mi[i][j-1],mi[i+(1<<(j-1))][j-1]);
}
int ask_mx(int l,int r)
{
int t=log2(r-l+1);
return max(mx[l][t],mx[r-(1<<t)+1][t]);
}
int ask_mi(int l,int r)
{
int t=log2(r-l+1);
return max(mi[l][t],mi[r-(1<<t)+1][t]);
}
signed main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
read(n),read(m);
for(int i=1;i<=n;i++)
read(a[i]),
sum[i]=sum[i-1]+a[i];
RMQ();
for(int l,r;m>=1;m--)
read(l),read(r),
write(ask_mx(l-1,r)+ask_mi(l-1,r)),puts("");
}
\(T4\) 罪人挽歌
前言
题面中说若不满足字典序最小则获得 \(50\%\) 分数,但是喵喵没有搬到 \(Special~judge\) ,所以用 \(50\%\) 的数据只有一组解代替。
题面说采用捆绑测试,防止直接输出 No
骗分,没有搬到捆绑测试,所以数据中压根没有 No
。
部分分
-
\(20pts\) :暴力枚举所有排列。
-
\(50pts\) :
假设他有解,考虑解的形状,发现若满足 \(a_i=a_{i-1}\) ,那么一定满足 \(b_i=b_{i+1}\) ,反之亦然。
考虑建图,\(a_i\) 与 \(b_i\) 连双向边。
跑欧拉回路 ,如果无法跑欧拉回路且懒得跑欧拉路径的话,不妨将两个奇点连起来,最后再删掉,就可以跑欧拉回路了。
显然如果奇点个数 \(\neq 0\) 且 \(\neq 2\) ,那就是无解了。
至于怎么跑欧拉回路在正解中说,如果欧拉回路直接打假了就是 \(20pts\) 。
那么如果欧拉回路复杂度没有假的话,因为不满足字典序最小,理论获得 \(50pts\) 。
-
\(70pts\) :
采用正解方法,但是欧拉回路复杂度假了。
实际上就是删边要直接删,不能惰性删。
点击查看代码
#include<bits/stdc++.h> #define int long long #define endl '\n' #define sort stable_sort using namespace std; const int N=2e6+10; template<typename Tp> inline void read(Tp&x) { x=0;register bool z=true; register char c=getchar(); for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0; for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48); x=(z?x:~x+1); } void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');} void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);} int n,s1,s2,d[N],a[N],b[N],cnt,ans1[N],ans2[N]; bool v[N]; stack<int>q; vector<int>e[N],f[N]; struct aa { int a,b; }to[N]; void dfs(int x,bool fa) { if(fa==0) { for(int i:e[x]) if(!v[i]) { v[i]=1; int y; if(x==to[i].a) y=to[i].b; else y=to[i].a; dfs(y,1); q.push(i); } } else { for(int i:f[x]) if(!v[i]) { v[i]=1; int y; if(x==to[i].a) y=to[i].b; else y=to[i].a; dfs(y,0); q.push(i); } } } signed main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif puts("Yes"); read(n); for(int i=1;i<=n;i++) read(a[i]),read(b[i]), to[i]={a[i],b[i]}, e[a[i]].push_back(i), f[b[i]].push_back(i), d[a[i]]++,d[b[i]]++; for(int i=1;i<=n;i++) if(d[i]==0) continue; else if(!s1&&(d[i]&1)) s1=i; else if(s1&&(d[i]&1)) { s2=i; break; } to[n+1]={s1,s2}, e[s1].push_back(n+1), e[s2].push_back(n+1); dfs(a[1],0); for(;!q.empty();q.pop()) if(q.top()!=n+1) ans1[++cnt]=q.top(); memset(v,0,sizeof(v)),cnt=0; dfs(b[1],1); for(;!q.empty();q.pop()) if(q.top()!=n+1) ans2[++cnt]=q.top(); for(int i=1;i<=n;i++) if(ans1[i]>ans2[i]) { for(int j=1;j<=n;j++) cout<<ans2[j]<<' '; return 0; } else if(ans1[i]<ans2[i]) { for(int j=1;j<=n;j++) cout<<ans1[j]<<' '; return 0; } for(int i=1;i<=n;i++) cout<<ans1[i]<<' '; }
正解
显然想要他字典序最小的话,选择从第一个边开始跑,那么就有两种情况,从 \(a_1\) 开始跑或从 \(b_1\) 开始跑。
如果后面的 \(dfs\) 也尽可能从靠前的边开始跑,那么显然就是字典序最小的了。
那么答案就是从 \(a_1\) 开始跑和从 \(b_1\) 开始跑两种情况字典序较小的一个。
接下来说这个欧拉回路怎么跑。
根据若 \(a_i=a_{i-1}\) 则一定 \(b_i=b_{i+1}\) ,我们应该蛇形跑。
也就是说如果这一次是 \(a→b\) ,那么下一次就 \(b→a\) ,反之亦然,处理一下他这次应该怎么跑就可以了。
同时这个欧拉回路和正常的不一样,他存的是边,所以栈里存的就是边。
不妨建图的时候记录每一条边连着哪两个点,然后开两个 \(vector\) ,一个存 \(a→b\) ,一个存 \(b→a\) ,将这条边压到两个点里,这样的话跑的时候也是边最小的。
另外不能使用惰性删,要直接删,惰性删的复杂度无法接受。
那么怎么直接删呢?板子如下:
void dfs(int x)
{
for(int i=id[x];i<e[x].size();i=max(i+1,id[x]))
{
if(e[x][i].second==0)
{
id[x]=i+1;
e[x][i].second=1;
dfs(e[x][i].first);
}
}
s.push(x);
}
当然开不开 \(pair\) 无所谓,开个 \(bool\) 也行,主要是这个 \(id\) 数组。
问题解决。
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define sort stable_sort
using namespace std;
const int N=2e6+10;
template<typename Tp> inline void read(Tp&x)
{
x=0;register bool z=true;
register char c=getchar();
for(;c<'0'||c>'9';c=getchar()) if(c=='-') z=0;
for(;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
x=(z?x:~x+1);
}
void wt(int x){if(x>9)wt(x/10);putchar((x%10)+'0');}
void write(int x){if(x<0)putchar('-'),x=~x+1;wt(x);}
int n,s1,s2,d[N],a[N],b[N],cnt,ans1[N],ans2[N],id[N],id2[N];
bool v[N];
stack<int>q;
vector<int>e[N],f[N];
struct aa
{
int a,b;
}to[N];
void dfs(int x,bool fa)
{
if(fa==0)
{
for(int i=id[x];i<e[x].size();i=max(id[x],i+1))
if(!v[e[x][i]])
{
id[x]=i+1;
v[e[x][i]]=1;
int s=e[x][i];
int y;
if(x==to[s].a) y=to[s].b;
else y=to[s].a;
dfs(y,1);
q.push(s);
}
}
else
{
for(int i=id2[x];i<f[x].size();i=max(id2[x],i+1))
if(!v[f[x][i]])
{
id2[x]=i+1;
v[f[x][i]]=1;
int s=f[x][i];
int y;
if(x==to[s].a) y=to[s].b;
else y=to[s].a;
dfs(y,0);
q.push(s);
}
}
}
signed main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
puts("Yes");
read(n);
for(int i=1;i<=n;i++)
read(a[i]),read(b[i]),
to[i]={a[i],b[i]},
e[a[i]].push_back(i),
f[b[i]].push_back(i),
d[a[i]]++,d[b[i]]++;
for(int i=1;i<=n;i++)
if(d[i]==0) continue;
else if(!s1&&(d[i]&1)) s1=i;
else if(s1&&(d[i]&1))
{
s2=i;
break;
}
to[n+1]={s1,s2},
e[s1].push_back(n+1),
e[s2].push_back(n+1);
dfs(a[1],0);
for(;!q.empty();q.pop())
if(q.top()!=n+1)
ans1[++cnt]=q.top();
memset(v,0,sizeof(v));
memset(id,0,sizeof(id));
memset(id2,0,sizeof(id2));
cnt=0;
dfs(b[1],1);
for(;!q.empty();q.pop())
if(q.top()!=n+1)
ans2[++cnt]=q.top();
for(int i=1;i<=n;i++)
if(ans1[i]>ans2[i])
{
for(int j=1;j<=n;j++)
cout<<ans2[j]<<' ';
return 0;
}
else if(ans1[i]<ans2[i])
{
for(int j=1;j<=n;j++)
cout<<ans1[j]<<' ';
return 0;
}
for(int i=1;i<=n;i++)
cout<<ans1[i]<<' ';
}