lhx 对 \((\ln n)^{\ln n}\) 求导求出一个形如 \(\frac{1}{n\ln n\ln\ln n}\) 的东西
A.图书管理
说一种很好玩的 \(n^2\log n\) 做法(因为动态中位数我只会这个)
对顶堆:
就是你开一个大根堆和一个小根堆,然后把它们怼在一起,钦定比中位数大的放小根堆,小的放大根堆,这样中间就是中位数
关于怎么才能知道基准是哪个数,其实你没必要知道基准是哪个数,因为你这是两个堆顶在一块了,你完全可以根据堆的特性来做,现在你只需要维护两个堆大小的平衡,这样就能保证找到中位数,加入插入某个数之后不平衡了,那么直接弹出较大的堆的堆顶,插入另一个堆(这样你还能保证两个堆的值域不交,除非堆顶相等,证明考虑直接想这个对顶堆的性质)
因此这玩意实现了动态插入,平衡,寻找中位数,可以用优先队列实现堆,复杂度均挂 \(\log\),\(2s\) 的话是稳过的,如果值域很大的话,这个做法是相当优秀的那一种
对顶堆写法
#include<bits/stdc++.h>
using namespace std;
template<typename T>
class single_mid_t{private:priority_queue<T,vector<T>,less<T>>p1;priority_queue<T,vector<T>,greater<T>>p2;inline void fixed(){while((int)p1.size()-(int)p2.size()>1){p2.push(p1.top());p1.pop();}while((int)p1.size()-(int)p2.size()<1){p1.push(p2.top());p2.pop();}}public:inline void insert(T x){p1.push(x);fixed();}inline T askmid(){return p1.top();}
};
int n;
long long ans;
int a[10001];
single_mid_t<int>s[10001];
int main(){scanf(n);for(int i=1;i<=n;++i){scanf(a[i]);}for(int i=1;i<=n;++i){ans+=1ll*i*i*a[i];s[i].insert(a[i]);for(int j=i+1;j+1<=n;j+=2){s[i].insert(a[j]);s[i].insert(a[j+1]);ans+=1ll*i*(j+1)*s[i].askmid();}}cout<<ans;
}
然后说 \(n^2\) 的正解
因为这个题是排列,你可以枚举中位数
钦定一个数 \(a_i\) 是中位数,考虑位每个地方赋值,令 \(a_j\lt a_i\) 处的 \(j\) 的值与 \(a_j\gt a_i\) 处的 \(j\) 的值相等,问题转化为求包含 \(i\) 的区间 \([l,r]\) 数量,满足区间和为 \(0\)
还是一样的,如果你钦定每处值的绝对值是 \(1\) 的话,这个值还是不会超过 \([-n,n]\),因此仍然考虑开桶,内层也可以 \(O(n)\) 做出来
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int dx=10000;
int n,ans;
int a[20001],cnt[20001];
signed main(){cin>>n;for(int i=1;i<=n;i++){cin>>a[i];}for(int i=1;i<=n;i++){int tot=0;for(int j=i;j<=n;++j){tot+=(a[j]>a[i])-(a[j]<a[i]);cnt[tot+dx]+=j;}tot=0;for(int j=i;j>=1;--j){tot-=(a[j]>a[i])-(a[j]<a[i]);ans+=j*cnt[tot+dx]*a[i];}tot=0;for(int j=i;j<=n;++j){tot+=(a[j]>a[i])-(a[j]<a[i]);cnt[tot+dx]&=0;}}cout<<ans;
}
B.两棵树
连通块数=剩余的点数−剩余的边数
贡献被拆成四个部分:点×点−边×点−点×边+边×边
-
点×点:选择 \(x\in T,u\in U\),当 \(x\neq u\) 均保留概率 \(\frac{1}{4}\),当 \(x=u\) 概率为 \(0\),所以期望为 \(\frac{n(n-1)}{4}\)
-
边×点:选择 \((x,t)\in T,u\in U\),当 \(x\neq u,y\neq u\) 均保留概率 \(\frac{1}{8}\),其余情况概率为 \(0\),所以期望为 \(\frac{(n-1)(n-2)}{8}\)
-
边×边:选择 \((x,y)\in T,(u,v)\in U\),当 \(x,y,u,v\) 互不相同时,概率为 \(frac{1}{16}\),其余情况概率为 \(0\),可以枚举所有 \((x,y)\),计算符合条件的边 \((u,v)\) 数量:\(n-1-\deg_Ux-\deg_Uy\),如果 \(U\) 存在 \((u,v)=(x,y)\) 则加一
奇怪的是似乎和我推出来不是很一样,为啥我推完之后剩一个 \(\frac{(n-1)^2}{2}\)
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int p=998244353;
inline int constexpr power(int a,int t=p-2){int ans=1,base=a;while(t){if(t&1){ans=ans*base%p;}base=base*base%p;t>>=1;}return ans;
}
int n,ans;
vector<int>t[200001],u[200001];
int fa[200001];
void dfs(int now,int last){fa[now]=last;for(int i:u[now]){if(i!=last){dfs(i,now);}}
}
signed main(){ios::sync_with_stdio(false);cin>>n;for(int i=1;i<=n-1;++i){int x,y;cin>>x>>y;t[x].push_back(y);t[y].push_back(x);}for(int i=1;i<=n-1;++i){int x,y;cin>>x>>y;u[x].push_back(y);u[y].push_back(x);}ans=(n-1)%p*power(2)%p;dfs(1,0);int res=0;for(int i=1;i<=n;++i){for(int j:t[i]){if(j>i){res=(res+n-1)%p;res=((res-(int)u[i].size()-(int)u[j].size())%p+p)%p;if(fa[i]==j or fa[j]==i) res=(res+1)%p;}}}ans=(ans+res*power(16)%p)%p;cout<<ans;
}
C.函数
好题
首先可以做有无解的判定
零点存在定理,找到所有数中 \(x_i\operatorname{xor}a-b\) 的最大值和最小值,如果这两个值同号则一定不存在解,异或值最大最小可以 01-trie 解决
然后继续零点存在定理,我们可以找到最大和最小的两个位置,现在这两个位置一定不同号,那么可以确定的是中间总存在一个位置合法(两个异号中间总有大于一个异号的连接点)
因此我们只需要维持这种端点异号的关系,每次二分中间点的值,每次用这个点替换同号的端点,最终一定会找到一个断点是合法的
#include<bits/stdc++.h>
using namespace std;
#define int long long
struct trie{signed to[1000001*32][2];signed id[1000001*32];signed cnt=0;inline const string to_string(int x){string ans;while(x){ans.push_back(x%2+'0');x/=2;}while(ans.size()!=31ull) ans.push_back('0');reverse(ans.begin(),ans.end());return ans;}inline void insert(int x,int _id){int pos=0;string y=to_string(x);for(char i:y){if(!to[pos][i-'0']){to[pos][i-'0']=++cnt;}pos=to[pos][i-'0'];}id[pos]=_id;}inline pair<int,int> askmax(int x){int pos=0,ans=0;string y=to_string(x);for(char i:y){ans*=2;if(to[pos][1-(i-'0')]){pos=to[pos][1-(i-'0')];ans++;}else{pos=to[pos][i-'0'];}}return {id[pos],ans};}inline pair<int,int> askmin(int x){int pos=0,ans=0;string y=to_string(x);for(char i:y){ans*=2;if(to[pos][i-'0']){pos=to[pos][i-'0'];}else{pos=to[pos][1-(i-'0')];ans++;}}return {id[pos],ans};}
};
trie t;
int n,q;
int x[1000001];
inline bool check(int i,int a,int b){return ((x[i]^a)-b)*((x[i+1]^a)-b)<=0;
}
inline int f(int x,int a,int b){return (x^a)-b;
}
void bl(){for(int i=1;i<=n;++i){cin>>x[i];}while(q--){int a,b;cin>>a>>b;bool flag=false;for(int i=1;i<n;++i){if(f(x[i],a,b)*f(x[i+1],a,b)<=0){cout<<i<<" ";flag=true;break;}}if(!flag) cout<<-1;cout<<'\n';}
}
signed main(){ios::sync_with_stdio(false);cin>>n>>q;for(int i=1;i<=n;++i){cin>>x[i];t.insert(x[i],i);}while(q--){int a,b;cin>>a>>b;auto tmpa=t.askmax(a),tmpb=t.askmin(a);if(n==1 or f(x[tmpa.first],a,b)*f(x[tmpb.first],a,b)>0){cout<<-1<<'\n';}else{if(tmpa.first>tmpb.first) swap(tmpa,tmpb);int l=tmpa.first,r=tmpb.first;while(l!=r-1){int mid=(l+r)/2;if(f(x[mid],a,b)<=0){if(tmpa.second-b<=0){l=mid;}else{r=mid;}}else{if(tmpa.second-b>0){l=mid;}else{r=mid;}}}cout<<l<<'\n';}}
}