Educational Codeforces Round 123 (Rated for Div. 2)思路分享
Educational Codeforces Round 123 (Rated for Div. 2)
本来这场的状态不太好,到后面磕不出来的时候有点瞌睡了,到最后竟然没有记rating....(本来还指望着通过这场掉一波分,这样就能报下一场的div2了,自我感觉div2现在还没有到拿捏的程度,E,F就没有当场做出来的时候....)
A. Doors and Keys
A题算是签到题吧,只需要判断钥匙与门的先后顺序即可。
B. Anti-Fibonacci Permutation
B题要求构造一个排列使得他为非斐波那契数列,要求构造出b个排列。
首先想到的就是倒序,之后发现我们将1依次向左移即可。
即:
4 3 2 1.
4 3 1 2
4 1 3 2
1 4 3 2
因为除了1之外,其他的元素都是倒序,注定符合\(a_i+a_{i+1}>a_{i+2}\)
C. Increase Subarray Sums
我承认我被这个题卡了许久,将近50分钟左右....
由于n是5000的缘故,所以我们完全可以枚举所有的区间去统计答案。可以发现对于某个\(f(k)\)而言,它的答案只可能由以下部分组成:
1.什么都不选,即答案为0.
2.选取区间长度恰好为k的区间,并将这个区间内的每个数都加上x。
3.选取区间长度小于k的区间,并将这个区间内的每个数都加上x。
4.选取区间长度大于k的区间,并将这个区间内的和增加kx.
第一部分不用考虑,我们只需要在最后的时候和0比大小即可。第二部分考虑我们枚举所有的区间的时候就可以办到了。第三部分其实就是f(0),f(1),f(2),...,f(k-1)取最大值,其实也好维护。
接下来就是第4部分了。我们令g(k)表示区间长度为k的区间的最大值。那么第四部分也好考虑就是max(g(k+1),g(k+2),...,g(n))+kx;
在我们枚举所有区间的时候后一方面维护第二部分的值,一方面维护g的即可。
但其实我们发现不用这么麻烦,我们完全可以正着做一次,倒着做一次dp即可。正着的不多解释,关于倒着的,考虑如果x的对应的最优区间长度为y的话,则x+1对应的长度也为y。因为sum(y)+kx>sum(z)+kx(z为x+1对应的最优区间长度).则对于f(x+1)的时候,显然存在sum(y)+(k+1)x>sum(z)+(k+1)x.我们完全可以只从x+1出转移即可。具体的可以参考代码:
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define db double
using namespace std;
const int N=5010;
int n,T,m,a[N];
ll b[N];
int main()
{
// freopen("1.in","r",stdin);
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
memset(b,0xef,sizeof(b));
for(int l=1;l<=n;++l)
{
ll sum=0;
for(int r=l;r<=n;++r)
{
sum+=a[r];
b[r-l+1]=max(b[r-l+1],sum+(r-l+1)*m);
}
}
for(int i=1;i<=n;++i) b[i]=max(b[i],b[i-1]);
for(int i=n;i>=0;--i) b[i]=max(b[i],b[i+1]-m);
for(int i=0;i<=n;++i) printf("%lld ",max(b[i],0ll));
puts("");
}
return 0;
}
D. Cross Coloring
这个题其实想到的话真的也不难。
每次涂颜色都会将一个行,一个列涂成同一个颜色。考虑每次操作我们都有k种选择,总的来说答案就是\(k^q\)。但是会有特殊的情况,即假设我在某次操作中,涂了1行1列,之后这些元素全部被其他颜色覆盖了,那我这次操作就没有任何意义了。所以我们只需要将这些操作删选出来即可。可以采用倒序的方法,记录下哪些行,列已经被涂过了,以及是否所有的行列都被涂过了。
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define db double
using namespace std;
const int N=2e5+10,P=998244353;
int n,T,m,q,k,vish[N],visl[N],x[N],y[N];
inline ll power(ll x,ll y)
{
ll ans=1;
while(y)
{
if(y&1) ans=ans*x%P;
y>>=1;
x=x*x%P;
}
return ans%P;
}
int main()
{
// freopen("1.in","r",stdin);
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d%d",&n,&m,&k,&q);
for(int i=1;i<=q;++i) scanf("%d%d",&x[i],&y[i]);
for(int i=1;i<=max(n,m);++i) vish[i]=visl[i]=0;
int cnt=0,cnth=0,cntl=0;
for(int i=q;i>=1;--i)
{
if((cntl==m||vish[x[i]])&&(cnth==n||visl[y[i]])) continue;
++cnt;
if(!vish[x[i]]) vish[x[i]]=1,cnth++;
if(!visl[y[i]]) visl[y[i]]=1,cntl++;
}
printf("%lld\n",power(k,cnt));
}
return 0;
}
E. Expand the Path
怎么说,这个题到最后还是没什么想法.....(还是我太菜了,又菜又爱生气.....)
之前的想法一直是全部的路径叠加到一起看有没有什么规律....发现没有(我太菜了..)....
但其实还有一种想法,就是我们可以统计下我们最多添加多少个D,添加多少个R。然后沿着原路径,对于每一个点来说如果他前面(包括它自己)只有D或只有R的话,说明我们在这个点的基础上将以他为端点的一行或一列的\(n-cnt_d或n-cnt_r\)个格点都覆盖掉,很简单的,我们可以在其基础上倍增D或R即可。之后在经过D与R之后的情况,我们可以通过倍增达到的区域就变成了一个\((n-cnt_d)\times(n-cnt_r)\)的长方形,每个点都对应一个矩形,我们的目的就是求所有的矩形的并。这可以直接采用扫描线的模板即可(正好我最怵这个东西,这次再写一遍..)。
PS:补一下为什么还有这种想法,我们考虑原路径中,我们中途加了p次R,k次D,到达了某个点(x,y).那么假如说我们没有增加这R和D的情况下,我们本应到达的点应为(x-k,y-p).而p,k则由我们能添加的最大R和D的关系限制。所以这种方法和题意中是一一对应的。换言之,题目中能通过的所有的点,一定是在原路径上的某个点之前(包括自身),通过扩展D和R来达到的。现在我们枚举所有的点,将它通过扩展能到达的所有点的并一定是答案。
点击查看代码
#include<bits/stdc++.h>
#define ls p<<1
#define rs p<<1|1
#define ll long long
using namespace std;
const int N=2e5+10;
char c[N];
int T,n,m,b[N<<1],num;
struct wy{int x,y1,y2,id;}a[N<<2];
struct Tree
{
int l,r,len,dat;
#define l(p) t[p].l
#define r(p) t[p].r
#define len(p) t[p].len
#define dat(p) t[p].dat
}t[N*20];
inline bool cmp(wy a,wy b) {return a.x<b.x;}
inline void build(int p,int l,int r)
{
l(p)=l;r(p)=r;
len(p)=0;dat(p)=0;
if(l==r) return;
int mid=l+r>>1;
build(ls,l,mid);
build(rs,mid+1,r);
}
inline int find(int x) {return lower_bound(b+1,b+num+1,x)-b;}
inline void pushup(int p)
{
if(l(p)==r(p))
{
len(p)=dat(p)?b[l(p)+1]-b[l(p)]:0;
return;
}
if(dat(p)) len(p)=b[r(p)+1]-b[l(p)];
else len(p)=len(ls)+len(rs);
}
inline void alter(int p,int l,int r,int x)
{
if(l<=l(p)&&r>=r(p))
{
dat(p)+=x;
pushup(p);
return;
}
int mid=l(p)+r(p)>>1;
if(l<=mid) alter(ls,l,r,x);
if(r>mid) alter(rs,l,r,x);
pushup(p);
}
int main()
{
// freopen("1.in","r",stdin);
scanf("%d",&T);
while(T--)
{
scanf("%d%s",&n,c+1);
m=strlen(c+1);
int cntr=0,cntd=0;
for(int i=1;i<=m;++i)
{
if(c[i]=='D') cntd++;
else cntr++;
}
bool f1=false,f2=false;
int x=1,y=1;num=0;
for(int i=1;i<=m;++i)
{
if(c[i]=='D') f1=true,x++;
if(c[i]=='R') f2=true,y++;
int j=2*i-1,k=2*i;
a[j].x=y;a[j].y1=x;a[j].y2=x+(f1?n-cntd:1);a[j].id=1;
a[k].x=y+(f2?n-cntr:1);a[k].y1=x;a[k].y2=x+(f1?n-cntd:1);a[k].id=-1;
b[++num]=x;b[++num]=x+(f1?n-cntd:1);
}
sort(a+1,a+2*m+1,cmp);
sort(b+1,b+num+1);
num=unique(b+1,b+num+1)-b-1;
build(1,1,num-1);
alter(1,find(a[1].y1),find(a[1].y2)-1,a[1].id);
ll ans=0;
for(int i=2;i<=2*m;++i)
{
ans+=(ll)len(1)*(a[i].x-a[i-1].x);
alter(1,find(a[i].y1),find(a[i].y2)-1,a[i].id);
}
printf("%lld\n",ans+1);
}
return 0;
}