Codeforces Round #602 (Div. 1, based on Technocup 2020 Elimination Round 3)
Codeforces Round #602 (Div. 1, based on Technocup 2020 Elimination Round 3)
A
其实你随便找一个满足要求的串,如果这个位置的括号正好是你的那个答案串的位置上的括号那么不用管,否则在后面找第一个满足要求的括号即可。我选的答案串形如:()()()()...((((()))))
。
\(code\) :
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <vector>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define pb(x) push_back(x)
#define MAXN 2010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define MOD 1000000007
#define ll long long
#define db double
using namespace std;
int read()
{
int w=0,flg=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
return w*flg;
}
char s[MAXN];
int n,k;
vector<pr >vc;
void solve()
{
vector<pr >__;swap(vc,__);
n=read(),k=read();
scanf("%s",s+1);
FUP(i,1,2*(k-1))
{
if(i%2==1)
{
if(s[i]=='(') continue;
FUP(j,i+1,n)
{
if(s[j]=='(')
{
vc.pb(mkp(i,j)),swap(s[i],s[j]);
break;
}
}
}
else
{
if(s[i]==')') continue;
FUP(j,i+1,n)
{
if(s[j]==')')
{
vc.pb(mkp(i,j)),swap(s[i],s[j]);
break;
}
}
}
}
//printf("## %d %d\n",2*(k-1)+1,(n-2*k)/2+2*(k-1));
FUP(i,2*(k-1)+1,(n-2*(k-1))/2+2*(k-1))
{
if(s[i]=='(') continue;
FUP(j,i+1,n)
{
if(s[j]=='(')
{
vc.pb(mkp(i,j)),swap(s[i],s[j]);
break;
}
}
}
printf("%d\n",(int)vc.size());
for(auto p:vc) printf("%d %d\n",p.fi,p.se);
}
int main(){
int T=read();
while(T--) solve();
return 0;
}
B
先考虑询问唯一的情况,那么我们显然是先加大的再加小的,如果有两个相同的那么先加靠前的那个。多个询问就把所有询问离线,按 \(k\) 从小到大排,双指针维护加入哪些数,然后第 \(pos\) 个位置可以考虑线段树二分,加入之后就把那个位置上变成 \(1\) 。
\(code\) :
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define pb(x) push_back(x)
#define MAXN 200010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define MOD 1000000007
#define ll long long
#define db double
using namespace std;
int read()
{
int w=0,flg=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
return w*flg;
}
int n,m,ans[MAXN],cur,a[MAXN];
pr p[MAXN];
struct Query{
int k,pos,id;
}que[MAXN];
bool cmp1(pr x,pr y){return x.fi>y.fi?true:x.fi<y.fi?false:x.se<y.se;}
bool cmp2(Query x,Query y){return x.k<y.k;}
#define lson (now<<1)
#define rson (now<<1|1)
#define mid ((l+r)>>1)
int sum[MAXN<<2];
void push_up(int now){sum[now]=sum[lson]+sum[rson];}
void update(int now,int l,int r,int x)
{
if(l==r) sum[now]=1;
else
{
if(x<=mid) update(lson,l,mid,x);
else update(rson,mid+1,r,x);
push_up(now);
}
}
int query(int now,int l,int r,int k)
{
if(l==r) return l;
else
{
if(sum[lson]>=k) return query(lson,l,mid,k);
else return query(rson,mid+1,r,k-sum[lson]);
}
}
int main(){
n=read();
FUP(i,1,n) p[i].fi=a[i]=read(),p[i].se=i;
sort(p+1,p+n+1,cmp1);
m=read();
FUP(i,1,m) que[i].k=read(),que[i].pos=read(),que[i].id=i;
sort(que+1,que+m+1,cmp2);
FUP(i,1,m)
{
while(cur<que[i].k) cur++,update(1,1,n,p[cur].se);
ans[que[i].id]=query(1,1,n,que[i].pos);
}
FUP(i,1,m) printf("%d\n",a[ans[i]]);
return 0;
}
C
显然答案满足单调性,考虑二分。 \(check\) 的时候我们对于每个点,如果他周围边长为 \(2\times mid+1\) 的正方形内部全是 \(1\) ,那么这个点初始为 \(1\) 显然不影响答案,这个可以用二维前缀和来判断。然后把这个正方形整体加一,这个可以二维差分。然后最后看一下有没有点在输入中是 \(1\) 但是没有被染到,有的话就是 false
。
\(code\) :
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <vector>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define pb(x) push_back(x)
#define MAXN 1000010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define MOD 1000000007
#define ll long long
#define db double
using namespace std;
int read()
{
int w=0,flg=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
return w*flg;
}
int n,m,ans;
vector<int>mp[MAXN],sum[MAXN],cf[MAXN];
bool ck(int x)
{
FUP(i,0,n) FUP(j,0,m) cf[i][j]=0;
FUP(i,1,n)
{
FUP(j,1,m)
{
int x1=i-x,y1=j-x,x2=i+x,y2=j+x;
if(x1<1||y1<1||x2>n||y2>m) continue;
if(sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1]!=(x2-x1+1)*(y2-y1+1)) continue;
//printf("i=%d j=%d x1=%d y1=%d x2=%d y2=%d\n",i,j,x1,y1,x2,y2);
cf[x1][y1]++;
if(y2<m) cf[x1][y2+1]--;
if(x2<n) cf[x2+1][y1]--;
if(x2<n&&y2<m) cf[x2+1][y2+1]++;
}
}
FUP(i,1,n) FUP(j,1,m) cf[i][j]+=cf[i-1][j]+cf[i][j-1]-cf[i-1][j-1];
FUP(i,1,n) FUP(j,1,m) if(!cf[i][j]&&mp[i][j]) return false;
return true;
}
int main(){
n=read(),m=read();
FUP(i,0,m) mp[0].pb(0),sum[0].pb(0),cf[0].pb(0);
FUP(i,1,n)
{
mp[i].pb(0),sum[i].pb(0),cf[i].pb(0);
FUP(j,1,m)
{
getchar()=='X'?mp[i].pb(1):mp[i].pb(0);
sum[i].pb(sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+mp[i][j]);
cf[i].pb(0);
}
getchar();
}
/*FUP(i,1,n)
{
FUP(j,1,m) printf("%d ",sum[i][j]);
puts("");
}*/
int l=0,r=1e6;
while(l<=r)
{
int mid=(l+r)>>1;
//printf("mid=%d\n",mid);
if(ck(mid)) l=mid+1,ans=mid;
else r=mid-1;
}
printf("%d\n",ans);
FUP(i,1,n)
{
FUP(j,1,m)
{
int x1=i-ans,y1=j-ans,x2=i+ans,y2=j+ans;
if(x1<1||y1<1||x2>n||y2>m) putchar('.');
else if(sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1]!=(x2-x1+1)*(y2-y1+1)) putchar('.');
else putchar('X');
}
puts("");
}
return 0;
}
D
考虑序列向右移等价于 \(h\) 向左移,然后你就发现每一位填的东西之间是独立的,定义 \(nxt_i=i\mod n+1\) ,那么对于 \(h_i=h_{nxt_i}\) 的位置来说,这一位填什么都对大小关系没有意义,所以这一位有 \(k\) 种填法。对于不一样的位置,要么填 \(h_i\) ,要么填 \(h_{nxt_i}\) ,要么填两个都不是的数。于是我们设有 \(p\) 个位置是 \(h_i=h_{nxt_i}\) ,则 \(Ans=k^p\times\sum_{i=0}^{n-p}\dbinom{n-p}{i}(k-2)^i\sum_{\lfloor\frac{n-p-i}{2}\rfloor+1\le j\le n-p-i}\dbinom{n-p-i}{j}\) ,后面这个 \(\sum\) 表示的是枚举有多少个位置填两个都不是的数。然后这个 \(\sum\) 实际上就是杨辉三角某一行的一半,可以根据奇偶性处理一些细节来求得。
\(code\) :
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define pb(x) push_back(x)
#define MAXN 200010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define MOD 998244353
#define ll long long
#define db double
using namespace std;
int read()
{
int w=0,flg=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
return w*flg;
}
ll poww(ll a,ll b)
{
ll ans=1,base=a;
while(b)
{
if(b&1) ans=ans*base%MOD;
base=base*base%MOD;
b>>=1;
}
return ans;
}
ll fac[MAXN],K,invfac[MAXN],ans;
ll C(int a,int b){return fac[a]*invfac[b]%MOD*invfac[a-b]%MOD;}
ll solve(int n,int k)
{
//printf("n=%d k=%d %lld\n",n,k,C(2,1));
ll re=0;
FUP(i,0,n-1)
{
ll add=poww(k,i)*C(n,i)%MOD;
//printf("add=%lld\n",add);
if(!((n-i)&1))
{
ll tmp=(poww(2,n-i)-C(n-i,(n-i)/2)+MOD)%MOD*poww(2,MOD-2)%MOD;
//printf("tmp=%lld\n",tmp);
add=add*tmp%MOD;
}
else add=add*poww(2,n-i-1)%MOD;
re=(re+add)%MOD;
}
return re;
}
int N,h[MAXN],cnt;
int main(){
N=read(),K=read();
FUP(i,1,N) h[i]=read();
FUP(i,1,N) if(h[i]==h[i%N+1]) cnt++;
fac[0]=1;
FUP(i,1,N) fac[i]=fac[i-1]*i%MOD;
invfac[N]=poww(fac[N],MOD-2);
FDW(i,N-1,0) invfac[i]=invfac[i+1]*(i+1)%MOD;
ans=poww(K,cnt)*solve(N-cnt,K-2)%MOD;
printf("%lld\n",ans);
return 0;
}
E
好强啊。考虑我们本质上要求构建一个 \((n+1)\times n\) 的 \(01\) 矩阵满足第 \(i\) 列上有 \(a_i\) 个 \(1\) ,显然我们把所有的数从大到小排序不影响构造方案,只需要再按照原来的排列放回去即可。然后对于排好序的序列,我们在第 \(i\) 列上从第 \(i\) 行开始从上往下填 \(1\) ,如果到了第 \(n+1\) 行还没填够就返回第一行继续填。为什么这是对的呢?考虑反证法,我们假设按照这样的填法有第 \(i,j(i< j)\) 行一个样,分两种情况:
\(\bullet\,\,\,i< j< n+1\) ,那么 \((i,i+1)=0\) ,于是 \((j,i+1)=0\) ,所以 \(a_{i+1}\le j-i-1\) 。然后如果 \(j=i+1\) 那么 \(a_{i+1}=0\) 不满足题意,否则考虑 \((i,i+2)\) 和 \((j,i+2)\) ,如果 \((i,i+2)=(j,i+2)=1\) 那么因为绕到上面了,所以 \(a_{i+2}=n\) 显然比 \(a_{i+1}\) 要大,类似的方法可以推出所有等于 \(1\) 的情况。如果是 \(0\) ,那么 \(a_{i+2}\le j-i-2\) 类似可以推得 \(a_{i+(j-i)}\le j-i-(j-i)=0\) ,不符合题意。
\(\bullet\,\,\,i< n+1,j=n+1\) ,那么 \((i,i)=(n+1,i)=1\) ,所以 \(a_{i-1}\ge a_i\ge n-i+2\) ,所以 \((i-1,i-1)=(n+1,i-1)=1\) ,于是 \(a_{i-1}\ge n-i+3\) ,然后推得 \(a_{i-(i-1)}=a_1\ge n+1\) 不符合题意。
值得注意的是,必须是从大到小。
\(code\) :
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<int,int>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define pb(x) push_back(x)
#define MAXN 1010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define MOD 1000000007
#define ll long long
#define db double
using namespace std;
int read()
{
int w=0,flg=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
return w*flg;
}
int n,a[MAXN],re[MAXN][MAXN],mx;
bool cmp(pr x,pr y){return x.fi>y.fi;}
pr p[MAXN];
int main(){
n=read();
FUP(i,1,n) a[i]=p[i].fi=read(),p[i].se=i;
sort(p+1,p+n+1,cmp);
FUP(i,1,n)
{
int cur=i,id=p[i].se;
while(p[i].fi) re[cur][id]=1,p[i].fi--,mx=max(mx,cur),cur=cur%(n+1)+1;
}
printf("%d\n",mx);
FUP(i,1,mx)
{
FUP(j,1,n) putchar('0'+re[i][j]);
puts("");
}
return 0;
}
F
考虑按照类似线段树的方式把他拆成几个长度为 \(2^k\) 的区间,并且这个区间内的数前面有几位是一样的,那么假设区间 \(A\) 定下了前 \(d1\) 位,区间 \(B\) 定下了前 \(d2\) 位,那么这两个区间异或后形成依然是一个相同性质的区间,并且定下的位数是 \(\min(d1,d2)\) ,这个很好考虑,因为对于都没定的位置,异或后形成的仍然是所有组合,对于一个定了,一个没定的位置,异或后还是所有组合,对于都定下的位置异或后仍然是固定的。于是一个朴素的想法,就是把每个区间都拆成 \(\log\) 个区间,然后把这两套 \(n\log\) 个区间两两组合形成 \((n\log)^2\) 个区间,排序后直接取并(看杜爷的题解学的这个取并的方法,感觉很妙很牛逼),但这样会 \(TLE\) ,考虑说既然两个区间异或定下的位数只与最小的有关,那么我么可以把那些多出来的位置看成没定下,也就是说在线段树上深度减小,于是我们可以把拆集合 \(B\) 中的区间的时候经过的路径上的所有结点在每一层上都记录下来,简单分析一下可以知道每个区间在每一层最多会经过 \(4\) 个区间。所以每一层的区间个数都是 \(O(n)\) 级别的,然后用拆好的 \(A\) 的 \(n\log\) 个区间在各自所在的层与 \(O(n)\) 个区间暴力匹配,这样总共会出来 \(n\log^2\) 个区间,然后排序后取并即可。
\(code\) :
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <vector>
#define FUP(i,x,y) for(int i=(x);i<=(y);i++)
#define FDW(i,x,y) for(int i=(x);i>=(y);i--)
#define FED(i,x) for(int i=head[x];i;i=ed[i].nxt)
#define pr pair<ll,ll>
#define mkp(a,b) make_pair(a,b)
#define fi first
#define se second
#define pb(x) push_back(x)
#define MAXN 100010
#define INF 0x3f3f3f3f
#define LLINF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
#define MOD 998244353
#define ll long long
#define db double
using namespace std;
ll read()
{
ll w=0,flg=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-'){flg=-1;}ch=getchar();}
while(ch<='9'&&ch>='0'){w=w*10+ch-'0',ch=getchar();}
return w*flg;
}
const ll inv=499122177;
void clean(vector<ll>&vc){vector<ll>__;swap(vc,__);}
int na,nb;
ll ans;
vector<pr >A,B,re;
vector<ll>mk[70];
#define mid ((l+r)>>1)
#define lson (now<<1)
#define rson (now<<1|1)
void solve1(ll now,ll l,ll r,ll ql,ll qr,ll d)
{
//printf("now=%lld l=%lld r=%lld ql=%lld qr=%lld d=%lld\n",now,l,r,ql,qr,d);
mk[d].pb(now);
if(ql<=l&&qr>=r) return;
if(ql<=mid) solve1(lson,l,mid,ql,qr,d+1);
if(qr>mid) solve1(rson,mid+1,r,ql,qr,d+1);
}
void solve2(ll now,ll l,ll r,ll ql,ll qr,ll d)
{
if(ql<=l&&qr>=r)
{
for(auto x:mk[d]) re.pb(mkp(now^x,d));
return;
}
if(ql<=mid) solve2(lson,l,mid,ql,qr,d+1);
if(qr>mid) solve2(rson,mid+1,r,ql,qr,d+1);
}
bool cmp(pr x,pr y){return x.fi<y.fi;}
int main(){
na=read();FUP(i,0,na-1) A.pb(mkp(0,0)),A[i].fi=read(),A[i].se=read();
nb=read();FUP(i,0,nb-1) B.pb(mkp(0,0)),B[i].fi=read(),B[i].se=read();
for(auto p:B) solve1(0,0,(1ll<<60)-1,p.fi,p.se,1);
for(auto p:A) solve2(0,0,(1ll<<60)-1,p.fi,p.se,1);
FUP(i,1,65) clean(mk[i]);
for(auto p:A) solve1(0,0,(1ll<<60)-1,p.fi,p.se,1);
for(auto p:B) solve2(0,0,(1ll<<60)-1,p.fi,p.se,1);
int N=re.size();
FUP(i,0,N-1)
{
ll l=re[i].fi<<(61-re[i].se),r=l+(1ll<<(61-re[i].se))-1;
//printf("l=%lld r=%lld %lld %lld\n",l,r,re[i].fi,re[i].se);
re[i].fi=l,re[i].se=r;
}
sort(re.begin(),re.end(),cmp);
ll l=re[0].fi,r=re[0].se;
FUP(i,1,N-1)
{
if(re[i].fi<=r) r=max(r,re[i].se);
else ans=(ans+(r+l)%MOD*(r%MOD-l%MOD+1+MOD)%MOD*inv%MOD)%MOD,l=re[i].fi,r=re[i].se;
}
ans=(ans+(r+l)%MOD*(r%MOD-l%MOD+1+MOD)%MOD*inv%MOD)%MOD;
printf("%lld\n",ans);
return 0;
}