CF1413 (Div.2)
硬生生把上分赛打成下分赛/baojin
$\text{A}$
注意到 $n$ 是偶数,直接将相邻的交换一下, $ans_{2i}=-a_{2i+1} \ ans_{2i+1}=a_{2i}$ 即可
$code$ :
#include<cstdio> #include<cctype> #define maxn 1111 inline int read(){ int r=0,f=0; char c; while(!isdigit(c=getchar()))f|=(c=='-'); while(isdigit(c))r=(r<<1)+(r<<3)+(c^48),c=getchar(); return f?-r:r; } inline int abs(int a){ return a<0?-a:a; } int n,a[maxn],ans[maxn]; inline void work(){ n=read(); for(int i=1;i<=n;i++)a[i]=read(); for(int i=1;i<=n;i+=2) ans[i]=-a[i+1],ans[i+1]=a[i]; for(int i=1;i<=n;i++)printf("%d ",ans[i]); puts(""); } int main(){ int t=read(); while(t--)work(); return 0; }
$\text{B}$
因为每个数不同,我们就找到第一列,然后按照第一列的顺序输出即可
#include<cstdio> #include<cctype> #define maxn 555 inline int read(){ int r=0,f=0; char c; while(!isdigit(c=getchar()))f|=(c=='-'); while(isdigit(c))r=(r<<1)+(r<<3)+(c^48),c=getchar(); return f?-r:r; } inline int abs(int a){ return a<0?-a:a; } int n,m,r[maxn][maxn],c[maxn][maxn],ans[maxn]; inline void work(){ n=read(),m=read(); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) r[i][j]=read(); for(int i=1;i<=m;i++) for(int j=1;j<=n;j++) c[i][j]=read(); for(int k=1;k<=m;k++){ bool ok=0; for(int i=1;i<=n;i++) if(r[i][1]==c[k][1]){ ok=1; break; } if(!ok)continue; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(c[k][i]==r[j][1])ans[i]=j; break; } for(int i=1;i<=n;i++,puts("")) for(int j=1;j<=m;j++) printf("%d ",r[ans[i]][j]); } int main(){ int t=read(); while(t--)work(); return 0; }
$\text{C}$
考试的时候双指针写挂了,就写了一个 $O(nlogn \times log10^9 \times 36)$ 的,但是少爷机让过了 /se
先将 $a$ , $b$ 排序,考虑二分答案,怎么 $check$ ?在里面任意找一个值做最大值(设为 $Max$ ),那么满足条件的最小值就很好求了( $Min=Max-ans$ )
然后对于 $\forall i \in [1,n]$ 我们要满足 $\exists j \in [1,6]$ 使得 $Min \le b_i-a_j \le Max$ ,同时加上 $a_j$ ,就是要满足 $Min+a_j \le b_i \le Max+a_j$
然后相当于有 $6$ 个区间,就看看区间之间(就是所有区间都覆盖不到的地方)有没有点( $b_i$ ),如果有就无法满足条件。
$code$ :
#include<cstdio> #include<cctype> #include<algorithm> using namespace std; #define maxn 101101 inline int read(){ int r=0,f=0; char c; while(!isdigit(c=getchar()))f|=(c=='-'); while(isdigit(c))r=(r<<1)+(r<<3)+(c^48),c=getchar(); return f?-r:r; } inline int min(int a,int b){ return a<b?a:b; } inline int max(int a,int b){ return a>b?a:b; } int n,a[11],b[maxn]; inline bool has(int l,int r){ return b[lower_bound(b+1,b+1+n,l)-b]<=r; } inline bool check(int x){ for(int i=1;i<=n;i++) for(int j=1;j<=6;j++){ int Max=b[i]-a[j]; int Min=Max-x; if(Min<0)continue; if(b[1]<Min+a[1])continue; if(b[n]>Max+a[6])continue; int lst=0; bool ok=1; for(int k=1;k<=6;k++){ if(lst>=Min+a[k]){ lst=Max+a[k]; continue; } if(has(lst+1,Min+a[k]-1)){ ok=0; break; } lst=Max+a[k]; } if(ok)return true; } return false; } int main(){ for(int i=1;i<=6;i++)a[i]=read(); sort(a+1,a+7); n=read(); for(int i=1;i<=n;i++)b[i]=read(); sort(b+1,b+1+n); int l=-1,r=b[n]-b[1]; while(l+1<r){ int mid=(l+r)>>1; if(check(mid))r=mid; else l=mid; } printf("%d\n",r); return 0; }
$\text{D}$
考虑用树状数组 + 栈来做,对于一个 $+$ 直接丢到栈里面去
对于一个 $-$ ,首先看货架上还有没有东西,再看前面的连续一段 $-$ 中的最大值有没有大于当前 $x$ ,如果都没有,就出栈存答案
然后就发现 $\text{WA} \ \text{on} \ \text{56}$ 了 /baojin ,我们发现没有考虑一种情况,可能连续一段 $-$ 确实没有大于 $x$ 的,但是因为这连续一段拿完了与上一段连续 $-$ 中间的 $+$ ,那么实际上还要与上一段的 $Max$ 取大,似乎可以用并查集维护,然而发现其实就是求当前栈顶元素的后缀最大值,用树状数组维护即可
$code$ :
#include<cstdio> #include<cctype> #define maxn 101101 #define lowbit(k) (k&(-k)) inline int read(){ int r=0,f=0; char c; while(!isdigit(c=getchar()))f|=(c=='-'); while(isdigit(c))r=(r<<1)+(r<<3)+(c^48),c=getchar(); return f?-r:r; } inline char get_c(){ char c=getchar(); while(c!='+'&&c!='-')c=getchar(); return c; } inline int max(int a,int b){ return a>b?a:b; } int n,cnt,top,c[maxn],cm[maxn],sta[maxn],ans[maxn]; inline void add(int k,int x){ for(;k<=n;k+=lowbit(k))c[k]+=x; } inline int sum(int k){ int s=0; for(;k>0;k-=lowbit(k))s+=c[k]; return s; } inline void addm(int k,int x){ for(;k>0;k-=lowbit(k))cm[k]=max(cm[k],x); } inline int askm(int k){ int Max=0; for(;k<=n;k+=lowbit(k))Max=max(Max,cm[k]); return Max; } int main(){ n=read(); for(int i=1;i<=n;i++)add(i,1); for(int i=1;i<=2*n;i++){ char c=get_c(); if(c=='+'){ cnt++; sta[++top]=cnt; continue; } if(!top)return puts("NO"),0; int x=read(); if(askm(sta[top])>x)return puts("NO"),0; if(sum(n)-sum(x-1)<top)return puts("NO"),0; ans[sta[top]]=x; addm(sta[top],x); add(x,-1); top--; } puts("YES"); for(int i=1;i<=n;i++)printf("%d ",ans[i]); return 0; }
$\text{E}$
显然,当 $a>b \times c$ 时,输出 $-1$
否则当 $c \le d$ 时,输出 $a$
接下来我能造成的伤害肯定要在 $c$ 时间内打完,所以考虑二分打的次数(其实可以直接算出来,但是懒得算)
然后注意, $check$ 应当是看 $a \le b \times d \times (mid-1)$ ,就是说我打这一次还会让怪物扣血,不然其实怪物是加血,最后答案会变劣
然后最后答案就是 $tim \times a - b \times d \times \frac{tim(tim-1)}{2}$ ,用等差数列算一下就可以算出该式
$code$ :
#include<cstdio> #include<cctype> inline int read(){ int r=0,f=0; char c; while(!isdigit(c=getchar()))f|=(c=='-'); while(isdigit(c))r=(r<<1)+(r<<3)+(c^48),c=getchar(); return f?-r:r; } int a,b,c,d; inline void work(){ a=read(),b=read(),c=read(),d=read(); if(a>1ll*b*c)return (void)puts("-1"); if(c<=d)return (void)printf("%d\n",a); int l=0,r=(c+d-1)/d+1; while(l+1<r){ int mid=(l+r)>>1; if(a>=1ll*d*b*(mid-1))l=mid; else r=mid; } printf("%lld\n",1ll*a*l-1ll*d*b*(l-1)*l/2); } int main(){ int t=read(); while(t--)work(); return 0; }