AtCoder Beginner Contest 282 题解(E-Ex)
AtCoder Beginner Contest 282 题解(E-Ex)
- Link:Tasks - AtCoder Beginner Contest 282
- 难度评分:\(7-72-104-1,154-1,604-1,734-2,412\)
下半篇章。
ABC282E Choose Two and Eat One(Diff. 1,154)
Solution:
对所有点之间里边,跑一个最大生成树即可。
Code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
struct edge{
int u,v,w;
}g[500010];
bool cmp(edge a,edge b){
return a.w>b.w;
}
int mod;
int ksm(int a,int b)
{
int ans=1;
a%=mod;
for(;b;b>>=1){
if(b&1) ans=(a*ans)%mod;
a=(a*a)%mod;
}
return ans;
}
int fa[500010];
int find(int x){
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
int idx;
int a[600];
int kruskal(){
int ans=0;
for(int i=1;i<=idx;i++){
int u=find(g[i].u);
int v=find(g[i].v);
if(u!=v){
fa[u]=v;
ans+=g[i].w;
}
}
return ans;
}
signed main(){
for(int i=1;i<=500000;i++) fa[i]=i;
int n;
cin>>n;
cin>>mod;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
for(int j=i+1;j<=n;j++){
g[++idx]=edge{i,j,(ksm(a[i],a[j])+ksm(a[j],a[i]))%mod};
}
}
sort(g+1,g+idx+1,cmp);
cout<<kruskal();
}
ABC282F Union of Two Sets (Diff. 1,604)
Solution:
显然每个端点都得有开始的线段
很容易想到回答询问的时候大概是左边一个区间,右边一个区间拼起来。
很想 ST 表的结构,我们倍增构造,每一个 \(st[i][j]\) 表示以 \(i\) 为开始,\(2^j\) 为长度,安排一个线段。
查询也是类似于 ST 表。
考虑线段条数的限制。
当 \(n=4,000\) 时,打表可得线段条数为 ,满足条件。
Code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
int st[4010][13];
int lg[4010],cnt;
void query(int x,int y){
int lga=lg[y-x+1];
cout<<st[x][lga]<<" "<<st[y-(1<<lga)+1][lga]<<endl;
cout.flush();
}
int n;
int ql[50010],qr[50010];
signed main(){
cin>>n;
for(int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
for(int j=0;(1<<j)<=n;j++){
for(int i=1;i+(1<<j)-1<=n;i++){
st[i][j]=++cnt;
ql[cnt]=i,qr[cnt]=i+(1<<j)-1;
}
}
cout<<cnt<<endl;
cout.flush();
for(int i=1;i<=cnt;i++){
cout<<ql[i]<<" "<<qr[i]<<endl;
cout.flush();
}
int q;
cin>>q;
for(int i=1;i<=q;i++){
int l,r;
cin>>l>>r;
query(l,r);
}
return 0;
}
ABC282G Similar Permutation (Diff.1734)
Solution:
令 \(dp[i][j][a][b]\) 表示当前枚举到第 \(i\) 位,相似度为 \(j\) ,第 \(i\) 位两个数在对应数组中排名为 \(a,b\) 的方案数。
二位前缀和优化 dp 即可。
Code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
int s[110][110],dp[2][110][110][110];
signed main(){
int n,k,p;
cin>>n>>k>>p;
int cur=1;
for(int a=1;a<=n;a++){
for(int b=1;b<=n;b++){
dp[cur][0][a][b]=1;
}
}
for(int i=2;i<=n;i++){
cur^=1;
memset(dp[cur],0,sizeof dp[cur]);
for(int j=0;j<i;j++){
int mx=n-i+2;
for(int a=1;a<=mx;a++){
for(int b=1;b<=mx;b++){
s[a][b]=(s[a-1][b]+s[a][b-1]-s[a-1][b-1]+dp[cur^1][j][a][b])%p;
s[a][b]=(s[a][b]+p)%p;
}
}
for(int a=1;a<mx;a++){
for(int b=1;b<mx;b++){
dp[cur][j][a][b]=(dp[cur][j][a][b]+s[a][mx]-s[a][b]+s[mx][b]-s[a][b])%p;
dp[cur][j][a][b]=(dp[cur][j][a][b]+p)%p;
//cout<<i<<" "<<cur<<" "<<j<<" "<<a<<" "<<b<<" "<<dp[cur][j][a][b]<<endl;
}
}
for(int a=1;a<mx;a++){
for(int b=1;b<mx;b++){
dp[cur][j+1][a][b]=(dp[cur][j+1][a][b]+s[a][b]+s[mx][mx]-s[a][mx]-s[mx][b]+s[a][b])%p;
dp[cur][j+1][a][b]=(dp[cur][j+1][a][b]+p)%p;
}
}
}
}
cout<<dp[cur][k][1][1]<<endl;
}
ABC282Ex Min + Sum(Diff. 2,412)
Solution:
看到寻找符合条件的区间,不难想到使用分治解决。
按照套路寻找左端点,找有哪些右端点符合条件。
设区间为 \([l,r]\),当前枚举的左端点为 \(lpos\),区间 \([l,mid]\) 的最小值是 \(lmin\),那么所有的右端点 \(i\) 可以按照 \([mid+1,i]\) 的最小值 \(rmin_i\) 与 \(lmin\) 的大小分成两类。
由于 \(rmin_i\) 单调非严格递减,且所以正好是左右两段。
-
\(rmin_i\ge lmin\) :这类情况下,区间 \([l,i]\) 的值为区间和加上 \(lmin\),由于区间和也是单调递增的,所以我们可以二分解决这一段。
-
\(rmin_i<lmin\):这类情况下区间的值不再具有单调性,我们对 \(b\) 数组求一个前缀和,那么区间和可以表示成 \(-pfs[lpos-1]+pfs[i]+rmin_i)\)。我们将与有半部分有关的提取出来,那么剩下的是 \(pfs[lpos-1]\) ,满足单调递减性质,所以我们可以把所有右端点存入一个数据结构里面,每此更新左端点时把所有已经不满足限制的删去即可。
不满足限制的情况有两种,一种是当前点在更新左端点后不属于第二类了,另外一种是在算答案贡献的时候已经不满足 \(s\) 的限制了,后面也不可能满足,删去。
查询则查询值小于等于阈值 \(s-pfs[lpos-1]\) 的个数即可。
不难想到可以用 std::set 维护。
时间复杂度 \(O(n\log^2n)\) 。
Code:
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
int n,m,a[300010],b[300010],ans;
int qzamin[300010],pfs[300010];
#define pr pair<int,int>
#define mp make_pair
void solve(int l,int r){
if(l==r){
ans+=((a[l]+b[l])<=m);
return;
}
int mid=(l+r)>>1;
solve(l,mid);
solve(mid+1,r);
int lemin=1e18,rxend=mid;
qzamin[mid]=1e18;
for(int i=mid+1;i<=r;i++) qzamin[i]=min(qzamin[i-1],a[i]);
set<pr> s1;
for(int i=mid+1;i<=r;i++) s1.insert(mp(-qzamin[i]-pfs[i]+pfs[mid],i));
for(int lpos=mid;lpos>=l;lpos--){
lemin=min(lemin,a[lpos]);
while(rxend<r&&qzamin[rxend+1]>=lemin){
rxend++;
auto it=s1.find(mp(-qzamin[rxend]-pfs[rxend]+pfs[mid],rxend));
if(it!=s1.end()) s1.erase(it);
}
int ql=mid+1,qr=rxend,cur=mid;
while(ql<=qr){
int md=(ql+qr)>>1;
if(pfs[md]-pfs[lpos-1]+lemin<=m){
cur=md,ql=md+1;
}else qr=md-1;
}
ans+=cur-mid;
while(!s1.empty()){
pr x=*s1.begin();
if((-x.first+pfs[mid]-pfs[lpos-1])<=m){
break;
}
s1.erase(s1.begin());
}
ans+=s1.size();
}
}
signed main(){
n=read(),m=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<=n;i++) b[i]=read(),pfs[i]=pfs[i-1]+b[i];
solve(1,n);
printf("%lld\n",ans);
return 0;
}

浙公网安备 33010602011771号