joke3579场,
T1 abc猜想 ([ARC111A] Simple Math 2)
直接 \(\bmod \ c\),很难做,不难想到换一个和 \(c\) 有关的模数,\(c^2\) 很好,因为它除以 \(c\) 再模上 \(c\) 后为 \(0\)。
求 \(x=a^b(\bmod\ c^2)\),\(a^b=k\cdot c^2+x\),除以 \(c\) 模 \(c\) 后,前面那个东西没了,只剩 \(x\),所以答案就是 \(\left\lfloor\frac{x}{c}\right\rfloor\)。
点击查看代码
#include<bits/stdc++.h>
#define int long long
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=1e4+10;
const int MOD=1e9+7;
std::vector<int> sth;
inline int qpow(int a,int b,int mod){
int res=1;
for(;b;b>>=1,a=a*a%mod)if(b&1)res=res*a%mod;
return res;
}
bool vis[N];
signed main(){
// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
int a=read(),b=read(),c=read();
int qc=c*c;
int ans=qpow(a,b,qc);
std::cout<<ans/c<<'\n';
}
T2 简单的排列最优化题 (CF819B Mister B and PR Shifts)
很容易的想到 \(n^2\) 做法,发现有很多东西都是重复记录了。考虑继承这些东西。
定义一个位置的价值为 \(a_i-i\),然后记一下这个价值出现的次数,发现每右移一位,这些价值的数量都会左移一位,即原来价值为 \(x\) 的现在价值为 \(x-1\)。
同时,答案可以借助这些价值从上一次的答案转移过来,即价值为非正整数的会对答案做出 \(1\) 的贡献,价值为正整数的做出 \(-1\) 的贡献。
此时只需要支持单点修改区间查询即可,树状数组是个不错的选择(其实再记一下其他东西可以 \(\mathcal{O}(n)\),不过 ds 不要脑子)。
对于每次的右移,开个变量记录一下就行,对于末尾的点单独算。
点击查看代码
#include<bits/stdc++.h>
#define int long long
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=4e6+10;
int t[N],len,n,a[N],last,ans=0,mink,minans;
inline void add(int pos,int x){int zc=pos+1;for(;pos<=len;pos+=pos&-pos)t[pos]+=x;}
inline int query(int l,int r){
int res=0;
l--;
for(;l;l-=l&-l)res-=t[l];
for(;r;r-=r&-r)res+=t[r];
return res;
}
inline int real(int x){return x+n+last;}
signed main(){
// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
n=read();len=n*4+1;
for(int i=1;i<=n;++i){
a[i]=read();
add(real(a[i]-i),1);ans+=std::abs(a[i]-i);
}
mink=0,minans=ans;
for(int i=1;i<=n-1;++i){
int en=a[n-i+1];
add(real(en-n),-1);
ans+=-(std::abs(en-n))+en-1+query(1,real(0))-query(real(1),real(n-1));
if(minans>ans){minans=ans,mink=i;}
last++;
add(real(en-1),1);
}
std::cout<<minans<<' '<<mink<<'\n';
}
T3 简单的线性做法题 (P4062 [Code+#1] Yazid 的新生舞会)
感觉是妙妙题,有较多做法,甚至有线性。这里介绍树状数组处理三阶前缀和的做法。
考虑对每个数单独处理。设 \(f_i\) 表示这个数到 \(i\) 位置时出现的次数,对于合法区间 \((l,r]\),\(f_r-f_l>\frac{r-l}{2}\Leftrightarrow 2f_r-r>2f_l-l\),直接拿树状数组维护 \(2f_i-i\) 的话,时间复杂度 \(\mathcal{O}(n^2\log n)\) 的。观察性质,发现对于每个数,它的 \(2f_i-i\) 是可以分段的。
简单观察发现,在两个相同数之间,它的 \(2f_i-i\) 是递减的,然后加一,然后又递减,而这些段的数量是 \(\mathcal{O}(n)\) 的,这启示我们去维护这些段。对于 \(a_i=2f_i-i\),要去前面找比他小的,\(T_i=\sum_{j=1}^i a_i\),\(W_i=\sum_{j=1}^i Ti\),树状数组上差分,设 \(d\) 为 \(a\) 的差分数组,有 \(a_i=\sum_{j=1}^i\)
开三个树状数组分别维护一下就好了,具体看代码。
点击查看代码
#include<bits/stdc++.h>
#define int long long
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=1e6+10;
int n,a[N],d[N],c,temp[N],ans,t1[N],t2[N],t3[N];
std::vector<int> p[N];
inline void add(int l,int r,int x){
if(l>r)return;
l+=n+1,r+=n+1;r++;
for(int i=l;i<=2*n+1;i+=i&-i)t1[i]+=x,t2[i]+=l*x,t3[i]+=(l*l-3*l)*x;
for(int i=r;i<=2*n+1;i+=i&-i)t1[i]-=x,t2[i]-=r*x,t3[i]-=(r*r-3*r)*x;
}
inline int query(int l,int r){
if(l>r)return 0;
int res1=0,res2=0;
l+=n+1,r+=n+1;l--;
for(int i=r;i;i-=i&-i)res1+=(((r+1)*(r+2))>>1)*t1[i]-r*t2[i]+t3[i]/2;
for(int i=l;i;i-=i&-i)res2+=(((l+1)*(l+2))>>1)*t1[i]-l*t2[i]+t3[i]/2;
return res1-res2;
}
inline void work(int x){
int num=0,last=0;
for(int i:p[x]){
num++;
int zc=num*2-i;
int l=zc-1,r=zc-1+i-1-last;
ans+=query(l-1,r-1);
add(l,r,1);
last=i;
}
int zc=num*2-n;
int l=zc,r=l+n-last;
ans+=query(l-1,r-1);
num=0;last=0;
for(int i:p[x]){
num++;
int zc=num*2-i;
int l=zc-1,r=zc-1+i-1-last;
add(l,r,-1);
last=i;
}
}
signed main(){
// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
n=read();
for(int i=1;i<=n;++i){temp[i]=a[i]=read();}
std::sort(temp+1,temp+1+n);
int len=std::unique(temp+1,temp+1+n)-temp-1;
for(int i=1;i<=n;++i)a[i]=std::lower_bound(temp+1,temp+1+len,a[i])-temp,p[a[i]].push_back(i);;
for(int i=1;i<=len;++i)work(i);
std::cout<<ans<<'\n';
}
T4 简单的线段树题
原题 P4145 上帝造题的七分钟 2 / 花神游历各国
这题太典了,开方到 \(1\) 后就没啥意义了,并且开不了多少次,直接魔改线段树维护即可。
点击查看代码
#include<bits/stdc++.h>
#define int long long
typedef long long ll;
typedef unsigned long long ull;
inline int read(){char ch=getchar();int x=0,f=1;for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;for(;ch>='0'&&ch<='9';ch=getchar())x=(x<<3)+(x<<1)+(ch^48);return x*f;}
const int N=1e6+10;
int a[N],n,m;
struct TREE{int num,sum;}t[N<<2];
inline void update(int p){
t[p].num=t[p<<1].num+t[p<<1|1].num;
t[p].sum=t[p<<1].sum+t[p<<1|1].sum;
}
inline void build(int p,int l,int r){
if(l==r){t[p]={a[l]==1?1:0,a[l]};return;}
int mid=l+r>>1;
build(p<<1,l,mid);build(p<<1|1,mid+1,r);
update(p);
}
inline void modi(int p,int l,int r,int x,int y){
if(l>=x&&r<=y){
if(t[p].num==r-l+1){return;}
int mid=l+r>>1;
if(l==r){t[p].sum=std::sqrt(t[p].sum);t[p].num=(t[p].sum==1?1:0);return;}
if(t[p<<1].num<mid-l+1)modi(p<<1,l,mid,x,y);
if(t[p<<1|1].num<r-mid)modi(p<<1|1,mid+1,r,x,y);
update(p);
return;
}
int mid=l+r>>1;
if(x<=mid)modi(p<<1,l,mid,x,y);
if(y>mid)modi(p<<1|1,mid+1,r,x,y);
update(p);
}
inline int query(int p,int l,int r,int x,int y){
if(l>=x&&r<=y)return t[p].sum;
int mid=l+r>>1,res=0;
if(x<=mid)res+=query(p<<1,l,mid,x,y);
if(y>mid)res+=query(p<<1|1,mid+1,r,x,y);
return res;
}
signed main(){
// freopen("in.in","r",stdin);freopen("out.out","w",stdout);
std::ios::sync_with_stdio(false);std::cin.tie(0);std::cout.tie(0);
n=read();
for(int i=1;i<=n;++i)a[i]=read();
build(1,1,n);
m=read();
for(int i=1;i<=m;++i){
int op=read(),l=read(),r=read();
if(op){std::cout<<query(1,1,n,l,r)<<'\n';}
else modi(1,1,n,l,r);
}
}