题解
A-排序是你的谎言
预计难度:1900-2100
思路
如果直接排序维护 xxx 的位置,复杂度是完全不能接受的,由于只关心 xxx 的位置,我们可以将所有小于等于 xxx 的数视为 000 , 所有大于 xxx 的数视为 111 ,即
ai={0,ai≤x1,ai>x a_i= \begin{cases} 0,\quad a_i\leq x\\ 1, \quad a_i>x \end{cases} ai={0,ai≤x1,ai>x
记 000 的个数为 cnt0cnt0cnt0 , 111 的个数为 cnt1cnt1cnt1 , posxposxposx 为 xxx 的位置 ,对于 [l,r][l,r][l,r] 上的两种操作可以等价于:
- 升序操作:将 [l,l+cnt0−1][l,l+cnt0-1][l,l+cnt0−1] 上的数变为 000 , 将 [l+cnt0,r][l+cnt0,r][l+cnt0,r] 上的数变为 111 , 若 posx∈[l,r]posx \in [l,r]posx∈[l,r] , 则操作之后 posx=l+cnt0−1posx=l+cnt0-1posx=l+cnt0−1
- 降序操作: 将 [l,l+cnt1−1][l,l+cnt1-1][l,l+cnt1−1] 上的数变为 111 , 将 [l+cnt1,r][l+cnt1,r][l+cnt1,r] 上的数变为 111若 posx∈[l,r]posx \in [l,r]posx∈[l,r] , 则操作之后 posx=l+cnt1posx=l+cnt1posx=l+cnt1
显然 cnt1cnt1cnt1 可以通过区间求和得到, 使用线段树即可 O(logn)O(logn)O(logn) 完成每次区间求和与区间修改操作
代码
需要注意的地方是懒标记的初始值需要设为 −1-1−1 , 以及 updateupdateupdate 时可能出现 r<lr<lr<l 的情况
cpp
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define ld long double
#define lc p<<1
#define rc p<<1|1
#define endl '\n'
#define all(a) a.begin()+1,a.end()
#define all0(a) a.begin(),a.end()
#define lowbit(a) (a&-a)
#define fi first
#define se second
#define pb push_back
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl
#define i128 __int128
using namespace std;
const double eps=1e-6;
const int inf=1e9;
const ll INF=1e18;
typedef pair<int,int>PII;
typedef array<int,3>PIII;
mt19937_64 rnd(time(0));
const int N=1e5+10;
int a[N];
int tr[N<<2];
int tag[N<<2];
int x;
void pushup(int p)
{
tr[p]=tr[lc]+tr[rc];
return;
}
void pushdown(int p,int l,int r)
{
if(tag[p]==-1) return;
int mid=(l+r)>>1;
tr[lc]=(mid-l+1)*tag[p];
tag[lc]=tag[p];
tr[rc]=(r-mid)*tag[p];
tag[rc]=tag[p];
tag[p]=-1;
return;
}
void build(int p,int l,int r)
{
tag[p]=-1;
if(l==r)
{
tr[p]=(a[l]<=x ? 0 : 1);
return;
}
int mid=(l+r)>>1;
build(lc,l,mid);
build(rc,mid+1,r);
pushup(p);
}
void update(int p,int l,int r,int ql,int qr,int val)
{
if(ql>qr) return;
if(ql<=l && r<=qr)
{
tr[p]=(r-l+1)*val;
tag[p]=val;
return;
}
pushdown(p,l,r);
int mid=(l+r)>>1;
if(ql<=mid) update(lc,l,mid,ql,qr,val);
if(qr>mid) update(rc,mid+1,r,ql,qr,val);
pushup(p);
return;
}
int query(int p,int l,int r,int ql,int qr)
{
if(ql<=l && r<=qr) return tr[p];
pushdown(p,l,r);
int mid=(l+r)>>1;
int sum=0;
if(ql<=mid) sum+=query(lc,l,mid,ql,qr);
if(qr>mid) sum+=query(rc,mid+1,r,ql,qr);
return sum;
}
void solve()
{
int n,m;
cin>>n>>m>>x;
int posx=0;
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(a[i]==x) posx=i;
}
build(1,1,n);
for(int i=1;i<=m;i++)
{
int op,l,r;
cin>>op>>l>>r;
int cnt1=query(1,1,n,l,r);//>x
int cnt0=r-l+1-cnt1;//<=x
if(l<=posx && r>=posx)
{
if(op==0) posx=l+cnt0-1;
else posx=l+cnt1;
}
if(op==0)
{
update(1,1,n,l,l+cnt0-1,0);
update(1,1,n,l+cnt0,r,1);
}
else
{
update(1,1,n,l,l+cnt1-1,1);
update(1,1,n,l+cnt1,r,0);
}
cout<<posx<<endl;
}
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t;cin>>t;
while(t--)
solve();
return 0;
}
E-嗣源大定理
预计难度:1000~1200
灵感来源于某次在数学吧看到的一个帖子

思路1
由于 aia_iai 在变化中可能会变得很大,所以用筛法是不可行的,事实上由于 aia_iai 会很快收敛并循环,所以直接使用最简单的 O(n)O(\sqrt n)O(n ) 分解质因数去模拟即可通过,唯一需要注意的地方就是开 long long
事实上该题在预期中是第二个签到,但是只有很少人尝试了这道题
代码1
cpp
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define ld long double
#define lc p<<1
#define rc p<<1|1
#define endl '\n'
#define all(a) a.begin()+1,a.end()
#define all0(a) a.begin(),a.end()
#define lowbit(a) (a&-a)
#define fi first
#define se second
#define pb push_back
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl
#define i128 __int128
using namespace std;
const double eps=1e-6;
const int inf=1e9;
const ll INF=1e18;
typedef pair<int,int>PII;
typedef array<int,3>PIII;
mt19937_64 rnd(time(0));
void solve()
{
ll s,k;cin>>s>>k;
auto get=[&](ll x)
{
ll mx=1;
for(ll i=2;i*i<=x;i++)
{
if(x%i==0)
{
mx=i;
while(x%i==0) x/=i;
}
}
if(x>1) mx=x;
return mx;
};
for(int i=1;i<=k;i++)
{
ll ne=get(s);
s=ne*10+ne%10;
}
cout<<s<<endl;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t;cin>>t;
while(t--)
solve();
return 0;
}
思路2
可以利用题面中提到的循环性质,只要得到了循环节的长度,就可以通过%运算快速计算出 aka_kak 的值,由于收敛的速度很快,前几项可以直接模拟得到,这种方法可以解决 kkk 达到 101210^{12}1012 时的数据
代码2
cpp
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define ld long double
#define lc p<<1
#define rc p<<1|1
#define endl '\n'
#define all(a) a.begin()+1,a.end()
#define all0(a) a.begin(),a.end()
#define lowbit(a) (a&-a)
#define fi first
#define se second
#define pb push_back
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl
#define i128 __int128
using namespace std;
const double eps=1e-6;
const int inf=1e9;
const ll INF=1e18;
typedef pair<int,int>PII;
typedef array<int,3>PIII;
mt19937_64 rnd(time(0));
void solve()
{
ll s,k;
cin>>s>>k;
map<ll,ll>vis;//第一次出现的位置
vector<ll>path;//数列
ll cur=s;
path.pb(cur);
vis[cur]=0;
auto get=[&](ll x)
{
ll mx=1;
for(ll i=2;i*i<=x;i++)
{
if(x%i==0)
{
mx=i;
while(x%i==0) x/=i;
}
}
if(x>1) mx=x;
return mx;
};
auto f=[&](ll x)
{
ll p=get(x);
return p*10+p%10;
};
for(ll i=1;i<=k;i++)
{
cur=f(cur);
if(vis.find(cur)!=vis.end())
{
int len1=vis[cur];//第一次出现的位置
int len2=i-len1;//循环节长度
int pos=(k-i)%len2;
cout<<path[len1+pos]<<endl;
return;
}
vis[cur]=i;
path.pb(cur);
}
cout<<cur<<endl;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t;cin>>t;
while(t--)
solve();
return 0;
}
I-太阳:镜中的幻影
预计难度:1600~1800
思路
海平面 HHH 从高向低下降,当 H≤hi,jH \le h_{i,j}H≤hi,j 时格子变成陆地。这等价于:我们将所有格子按海拔高度 hi,jh_{i,j}hi,j 从大到小排序,依次将格子"激活"为陆地,使用并查集动态维护当前陆地组成的连通分量(岛屿)个数即可。
具体来说每激活一块陆地就将岛屿个数 cntcntcnt 加1 , 然后检查其四周的陆地,若其四周的陆地已经激活且与其属于不同集合则将 cntcntcnt 减1 , 若处理完一批相同高度的陆地之后 cnt=kcnt=kcnt=k 则当前高度为合法高度。由于要维护合法区间,我们还需要记录上一次的合法高度 last 以及判断上一次的高度是否处于合法区间之中,
- 若 cnt=kcnt=kcnt=k 且之前高度不属于合法区间之内,说明当前高度为一个合法区间的右端点(上限)
- 若 cnt!=kcnt!=kcnt!=k 且之前高度属于合法区间之内,说明当前高加1为上一个合法区间的左端点(下限)
需要注意的是若最后一个高度被处理完之后仍处在合法区间内(cnt始终为k),则说明该合法区间的左端点为1
代码
cpp
#include<bits/stdc++.h>
#define ull unsigned long long
#define ll long long
#define ld long double
#define lc p<<1
#define rc p<<1|1
#define endl '\n'
#define all(a) a.begin()+1,a.end()
#define all0(a) a.begin(),a.end()
#define lowbit(a) (a&-a)
#define fi first
#define se second
#define pb push_back
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl
#define i128 __int128
using namespace std;
const double eps=1e-6;
const int inf=1e9;
const ll INF=1e18;
typedef pair<int,int>PII;
typedef array<int,3>PIII;
mt19937_64 rnd(time(0));
const int N=1e6+10;
int p[N];
int vis[N];
int find(int x)
{
if(x!=p[x]) p[x]=find(p[x]);
return p[x];
}
struct node{
int h,x,y;
bool operator < (const node &t)const
{
return h>t.h;
}
};
int dx[]={0,1,0,-1};
int dy[]={-1,0,1,0};
void solve()
{
int n,m,k;
cin>>n>>m>>k;
for(int i=1;i<=n*m;i++) {p[i]=i;vis[i]=0;}
vector<node>a(n*m+1);
int id=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
int x;cin>>x;
a[id++]={x,i,j};
}
}
sort(all(a));
int cnt=0;
auto get=[&](int x,int y)
{
return (x-1)*m+y;
};
vector<PII>ans;
int last=-1;
int ok=0;
for(int i=1;i<=n*m;)
{
int j=i;
int curh=a[i].h;
while(j<=n*m && a[j].h==curh)
{
int pos=get(a[j].x,a[j].y);
vis[pos]=1;
cnt++;
j++;
}
for(int t=i;t<j;t++)
{
int id1=get(a[t].x,a[t].y);
for(int d=0;d<4;d++)
{
int nx=a[t].x+dx[d];
int ny=a[t].y+dy[d];
int id2=get(nx,ny);
if(nx<1 || nx>n || ny<1 || ny>m) continue;
if(!vis[id2]) continue;
int px=find(id1);
int py=find(id2);
if(px!=py)
{
p[px]=py;
cnt--;
}
}
}
if(cnt==k)
{
if(!ok)
{
ok=1;
last=curh;
}
}
else
{
if(ok)
{
ans.pb({curh+1,last});
ok=0;
}
}
i=j;
}
if(ok) ans.pb({1,last});
if(!ans.size()) {cout<<-1<<endl;return;}
sort(all0(ans));
cout<<ans.size()<<endl;
for(auto [l,r]:ans) cout<<l<<" "<<r<<endl;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t;cin>>t;
while(t--)
solve();
return 0;
}