用户工具

站点工具


2020-2021:teams:legal_string:jxm2001:other:错题集_5

差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
2020-2021:teams:legal_string:jxm2001:other:错题集_5 [2021/03/03 18:26]
jxm2001
2020-2021:teams:legal_string:jxm2001:other:错题集_5 [2021/08/24 11:25] (当前版本)
jxm2001
行 37: 行 37:
 考虑点分治,设当前重心为 $\text{rt}$,于是对点对 $(s,​t)$,需要判定 $a_s+a_{v_1}x^1+a_{v_2}x^2+\cdots a_tx^k\equiv y(\bmod p)$。 考虑点分治,设当前重心为 $\text{rt}$,于是对点对 $(s,​t)$,需要判定 $a_s+a_{v_1}x^1+a_{v_2}x^2+\cdots a_tx^k\equiv y(\bmod p)$。
  
-移项,得 $a_{v_k'​}x^{k'​}+\cdots +a_tx^k\equiv y-(a_s+a_{v_1}x^1+a_{v_2}x^2+\cdots a_{\text{rt}}x^{k'​-1})(\bmod p)$+移项,得 $a_{v_k'​+1}x^{k'+1}+\cdots +a_tx^k\equiv y-(a_s+a_{v_1}x^1+a_{v_2}x^2+\cdots a_{\text{rt}}x^{k'​})(\bmod p)$ 
 + 
 +设 $\text{pre}(s)=a_s+a_{v_1}x^1+a_{v_2}x^2+\cdots a_{\text{rt}}x^k,​\text{suf}(s)=a_{rt}+a_{v_1}x^1+\cdots +a_sx^k$,于是上式化简为 
 + 
 +$$ 
 +\text{suf}(s)-a_{rt}\equiv (y-\text{pre}(s))x^{-k'​}(\bmod p
 +$$ 
 + 
 +于是 $\text{dfs}$ 过程中维护 $\text{pre}(s),​\text{suf}(s)$ 同时记录 $\text{suf}(s)-a_{rt}$ 和 $(y-\text{pre}(s))x^{-k'​}$,最后双指针统计答案。 
 + 
 +另外关于两个结点都在同一个子树的情况,直接容斥即可。总时间复杂度为 $O(n\log^2 n)$
  
 <hidden 查看代码>​ <hidden 查看代码>​
 <code cpp> <code cpp>
 +const int MAXN=1e5+5;
 +int x,​px[MAXN],​invx[MAXN],​y,​Mod;​
 +int quick_pow(int a,int k){
 + int ans=1;
 + while(k){
 + if(k&​1)ans=1LL*ans*a%Mod;​
 + a=1LL*a*a%Mod;​
 + k>>​=1;​
 + }
 + return ans;
 +}
 +struct Edge{
 + int to,next;
 +}edge[MAXN<<​1];​
 +int head[MAXN],​edge_cnt;​
 +void Insert(int u,int v){
 + edge[++edge_cnt]=Edge{v,​head[u]};​
 + head[u]=edge_cnt;​
 +}
 +int val[MAXN],​in0[MAXN],​in1[MAXN],​out0[MAXN],​out1[MAXN];​
 +int sz[MAXN],​mson[MAXN],​tot_sz,​root,​root_sz;​
 +bool vis[MAXN];
 +void find_root(int u,int fa){
 + sz[u]=1;​mson[u]=0;​
 + for(int i=head[u];​i;​i=edge[i].next){
 + int v=edge[i].to;​
 + if(vis[v]||v==fa)
 + continue;
 + find_root(v,​u);​
 + sz[u]+=sz[v];​
 + mson[u]=max(mson[u],​sz[v]);​
 + }
 + mson[u]=max(mson[u],​tot_sz-sz[u]);​
 + if(mson[u]<​root_sz){
 + root=u;
 + root_sz=mson[u];​
 + }
 +}
 +vector<​pair<​int,​int>​ >​vec1,​vec2;​
 +void update(int u,int dep,int &​pre,​int &suf){
 + pre=(1LL*pre*x+val[u])%Mod;​
 + suf=(suf+1LL*px[dep]*val[u])%Mod;​
 + vec1.push_back(make_pair(1LL*(y-pre+Mod)*invx[dep]%Mod,​u));​
 + vec2.push_back(make_pair((suf-val[root]+Mod)%Mod,​u));​
 +}
 +void dfs(int u,int fa,int dep,int pre,int suf){
 + update(u,​dep,​pre,​suf);​
 + for(int i=head[u];​i;​i=edge[i].next){
 + int v=edge[i].to;​
 + if(vis[v]||v==fa)continue;​
 + dfs(v,​u,​dep+1,​pre,​suf);​
 + }
 +}
 +void cal(int u,int sign,int dep,int pre,int suf){
 + vec1.clear();​
 + vec2.clear();​
 + update(u,​dep,​pre,​suf);​
 + for(int i=head[u];​i;​i=edge[i].next){
 + int v=edge[i].to;​
 + if(vis[v])continue;​
 + dfs(v,​u,​dep+1,​pre,​suf);​
 + }
 + sort(vec1.begin(),​vec1.end());​
 + sort(vec2.begin(),​vec2.end());​
 + for(int pos1=0,​pos2=0,​cnt;​pos1<​vec1.size();​pos1++){
 + if(pos1==0||vec1[pos1].first!=vec1[pos1-1].first)cnt=0;​
 + while(pos2<​vec2.size()&&​vec2[pos2].first<​vec1[pos1].first)pos2++;​
 + while(pos2<​vec2.size()&&​vec2[pos2].first==vec1[pos1].first)pos2++,​cnt++;​
 + out1[vec1[pos1].second]+=cnt*sign;​
 + }
 + for(int pos1=0,​pos2=0,​cnt;​pos2<​vec2.size();​pos2++){
 + if(pos2==0||vec2[pos2].first!=vec2[pos2-1].first)cnt=0;​
 + while(pos1<​vec1.size()&&​vec1[pos1].first<​vec2[pos2].first)pos1++;​
 + while(pos1<​vec1.size()&&​vec1[pos1].first==vec2[pos2].first)pos1++,​cnt++;​
 + in1[vec2[pos2].second]+=cnt*sign;​
 + }
 +}
 +void query(int u){
 + cal(u,​1,​0,​0,​0);​
 + for(int i=head[u];​i;​i=edge[i].next){
 + int v=edge[i].to;​
 + if(vis[v])continue;​
 + cal(v,​-1,​1,​val[u],​val[u]);​
 + }
 +}
 +void solve(int u){
 + int cur_sz=tot_sz;​
 + vis[u]=true;​query(u);​
 + for(int i=head[u];​i;​i=edge[i].next){
 + int v=edge[i].to;​
 + if(vis[v])
 + continue;
 + tot_sz=sz[v]>​sz[u]?​cur_sz-sz[u]:​sz[v];​root_sz=MAXN;​
 + find_root(v,​u);​
 + solve(root);​
 + }
 +}
 +int main()
 +{
 + int n=read_int();​
 + Mod=read_int(),​x=read_int(),​y=read_int();​
 + _rep(i,​1,​n)val[i]=read_int();​
 + _for(i,​1,​n){
 + int u=read_int(),​v=read_int();​
 + Insert(u,​v);​
 + Insert(v,​u);​
 + }
 + px[0]=1;
 + _for(i,​1,​MAXN)px[i]=1LL*px[i-1]*x%Mod;​
 + invx[MAXN-1]=quick_pow(px[MAXN-1],​Mod-2);​
 + for(int i=MAXN-1;​i;​i--)invx[i-1]=1LL*invx[i]*x%Mod;​
 + tot_sz=n,​root_sz=MAXN;​
 + find_root(1,​0);​
 + solve(root);​
 + LL ans=0;
 + _rep(i,​1,​n){
 + in0[i]=n-in1[i];​
 + out0[i]=n-out1[i];​
 + ans+=2LL*in0[i]*in1[i];​
 + ans+=2LL*out0[i]*out1[i];​
 + ans+=1LL*in0[i]*out1[i];​
 + ans+=1LL*in1[i]*out0[i];​
 + }
 + enter(1LL*n*n*n-ans/​2);​
 + return 0;
 +}
 +</​code>​
 +</​hidden>​
  
 +===== 2、Trips =====
 +
 +[[https://​codeforces.com/​contest/​1037/​problem/​E|链接]]
 +
 +==== 题意 ====
 +
 +给定一个图,初始时图没有边,接下来每次一个加边操作,然后询问加边后满足所有点度数不小于 $k$ 的最大子图。
 +
 +==== 题解 ====
 +
 +考虑先加入所有边,然后从最后的操作开始向前删边。
 +
 +对每个点,当该点度数小于 $k$ 时直接删去该点和对应边。每个点仅删去一次,每条边最多访问三次,于是总时间复杂度 $O(n+m)$。
 +
 +<hidden 查看代码>​
 +<code cpp>
 +const int MAXN=2e5+5;
 +struct Edge{
 + int u,v;
 + bool vis;
 +}edge[MAXN];​
 +vector<​pair<​int,​int>​ > g[MAXN];
 +int deg[MAXN],​ans,​k;​
 +bool live[MAXN];
 +void update(int u);
 +void del(int u){
 + ans--;
 + live[u]=false;​
 + _for(i,​0,​g[u].size()){
 + int v=g[u][i].first,​edge_id=g[u][i].second;​
 + if(!edge[edge_id].vis){
 + edge[edge_id].vis=true;​
 + update(v);​
 + }
 + }
 +}
 +void update(int u){
 + if(live[u]){
 + deg[u]--;
 + if(deg[u]<​k)
 + del(u);
 + }
 +}
 +int main()
 +{
 + int n=read_int(),​m=read_int();​
 + k=read_int();​
 + ans=n;
 + _rep(i,​1,​n)live[i]=true;​
 + _for(i,​0,​m){
 + int u=read_int(),​v=read_int();​
 + edge[i]=Edge{u,​v};​
 + g[u].push_back(make_pair(v,​i));​
 + g[v].push_back(make_pair(u,​i));​
 + deg[u]++;
 + deg[v]++;
 + }
 + _rep(i,​1,​n){
 + if(live[i]&&​deg[i]<​k)
 + del(i);
 + }
 + stack<​int>​s;​
 + for(int i=m-1;​i>​=0;​i--){
 + s.push(ans);​
 + if(edge[i].vis)continue;​
 + edge[i].vis=true;​
 + update(edge[i].u);​
 + update(edge[i].v);​
 + }
 + while(!s.empty()){
 + enter(s.top());​
 + s.pop();
 + }
 + return 0;
 +}
 </​code>​ </​code>​
 </​hidden>​ </​hidden>​
 +
 +===== 3、CDN流量调度问题 =====
 +
 +[[https://​acm.hdu.edu.cn/​contests/​contest_showproblem.php?​pid=1003&​cid=1029|2021CCPC华为云挑战赛 1003]]
 +
 +==== 题意 ====
 +
 +给定 $n$ 个网络和 $m$ 个额外结点。第 $i$ 个网络初始有一个结点,且最多被分配 $b_i$ 个结点,当被分配 $k$ 个结点时,费用为 $\lceil\frac {a_i}k\rceil$。
 +
 +最小化所有网络的费用和。
 +
 +==== 题解 ====
 +
 +易知使得每个网络的费用发生变化的 $k$ 只有 $O(\sqrt {a_i})$ 个,可以 $O\left(\sum_{i=1}^n a_i\right)$ 暴力预处理。
 +
 +然后设 $\text{dp}(i,​j)$ 表示只考虑前 $i$ 个网络花费 $j$ 个额外结点的最小费用,然后状态转移只枚举有效的 $k$。
 +
 +总时间复杂度 $O(nm\sqrt a)$,个人感觉时间复杂度过大,但题目测试数据貌似比较水,所以可以过。
 +
 +ps. 本人一开始考虑优先队列,贪心取变化量最大的网络加结点,但实际受取整函数影响,变化量不是单调的,因此假了。
2020-2021/teams/legal_string/jxm2001/other/错题集_5.1614767167.txt.gz · 最后更改: 2021/03/03 18:26 由 jxm2001