[考试反思]0424省选模拟79:判定

挺好。果然有瞎猜结论+有部分分 然后我就活了。

$T3$看大样例瞎猜了个结论,然后$T1,2$就写了个没啥水平的暴力,混个名次跑路。

其实$T2$的大数学题没怎么花时间推,不然可能有戏。主要时间丢在$T1$了

(自己数据结构啥水平不知道啊为啥非要跟数据结构作对啊

(好吧因为好像数学也不怎么着

为啥他们都会正解啊。。。我怕不是缺个脑子。。

 

 

T1:区间第k小

大意:给定常数$w$,在线多次询问忽视区间中出现次数$>w$的数后的区间第$k$大值。$n,q,w,A_i \le 10^5$

题解思路:

我们需要解决的问题是:区间$[l,r]$内,小于等于$x$的出现次数$\le w$的数的出现总次数是多少。考虑每个数的贡献。

可以认为,区间中每个数的最后$w$次出现的贡献都是$1$,倒数第$w+1$次的贡献是$-w$,其余贡献为$0$。

按值域建权值线段树,权值线段树的每个节点都是一个主席树,主席树的下标是左端点,根节点标号为右端点(离散)

每次右端点右移时最多三个位置改变(一个$0 \rightarrow 1$一个$1 \rightarrow -w$一个$-w \rightarrow 0$)

一个$trick$是只对作为左儿子的节点维护主席树,常数小得多。

建树和查询都是$O(nlog\ n)$的。

 

神仙牛的大神分块思路:

对值域和下标分别分块然后维护下面这些东西:

$pre[i][j]$表示前$i$个下标块中数字$j$出现了几次。

$bib[l][r][x]$表示对于第$[l,r]$这些下标块,值域在$x$值域块中的数的贡献(出现超过$w$次则为$0$)

这两个东西都可以$O(n^{1.5})$维护出来。($bib$是枚举$l$然后扫整个序列一遍)

用这个东西来解决询问:

首先对于询问$[l,r]$直接用$bib$更新整下标块中每个权值块的贡献数。$O(\sqrt{n})$

然后把零散块的部分依次加入来进行贡献(记录每个值出现了多少次,更新每个块的贡献)。$O(\sqrt{n})$

然后就可以枚举每个块,得到答案的值在哪个值域块里。

然后再枚举这个值域块的每一个权值,用$pre$查询出现次数,最终就可以得到排名对应的数。

然而肯定不能对于每个询问都更新每个值的出现次数,所以写个$tag$表示初值就是$[l,r]$之间整块的出现次数。

总时间复杂度$O(n^{1.5})$

 

T2:求和

大意:设$f_d(x)=\sum\limits_{i=1}^{x} (-1)^{x的质因子个数} [x无大于 >d 次的质因子]$。求$\sum\limits_{i=1}^{n} \sum\limits_{j=1}^{n} \sum\limits_{d=1}^{k} f_d(gcd(i,j))$

$n \le 10^{10},k \le 40$

首先我们考虑一个线性的暴力:设$C(x)=\sum\limits_{d=1}^{k} f_d(x)$。那么$C(x)=(-1)^{tot(x)}max(0,k+1-mx(x))$。

其中$mx(x)$表示$x$的质因子最大次数,$tot(X)$表示质因子总次数。

这显然可以线筛出质因子次数然后$O(n)$计算。

对题目中的式子进行莫比乌斯反演,得到$\sum\limits_{i=1}^{n} \sum\limits_{d=1}^{k} f_d(i) (-1+\sum\limits_{j=1}^{\frac{n}{i}}\varphi(j) )$

后面的部分可以直接杜教筛。设$F_d(x) = \sum\limits_{i=1}^{x} f_d(x)$.问题在于求出$\sum\limits_{d=1}^{k} F_d(n)$

容斥一下如果含有超过$d$次的因子,那么枚举这个因子$i$,首先卡住上界有$i^{d+1} \le n$。

设$g(x)=f_{\infty}(x)$。也就是$(-1)^{mx(x)}$。这个东西当然是完全积性函数。以及设$G(x)=\sum\limits_{i=1}^{x} g(i) $那么有

$F_d(x) = \sum\limits_{i=1}^{\sqrt[d+1]{n}} g(i)^{d+1} \mu(i) G(\frac{n}{i^{d+1}})$

这个$d$枚举界很小直接枚举就行问题在于求$G$

然后发现$G$卷积上$I$得到的就是$[\exists i,i^2|n]$。所以就也可以杜教筛了。

复杂度大概是$O(n^{\frac{2}{3}})$。如果卡常的话,把较小的$n$的贡献用前面的暴力$O(n)$跑出来而不是$O(nk)$。常数小了一半。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define S 10000019
 4 #define ll long long
 5 int mu[S],phi[S],g[S],k,p[S],pc,ans,lt[S],mt[S]; ll n,pw[100001][42]; bool np[S];
 6 const int R=(1<<30)-1;
 7 struct hash_map{
 8     int fir[S],l[S],w[S],ec;ll to[S];
 9     int&operator[](ll x){ int r=x%S;
10         for(int i=fir[r];i;i=l[i])if(to[i]==x)return w[i];
11         l[++ec]=fir[r];fir[r]=ec;to[ec]=x;return w[ec]=-1;
12     }
13 }Mphi,Mg;
14 int G(ll n){
15     if(n<S)return g[n];
16     int&v=Mg[n]; if(~v)return v; v=sqrt(n);
17     for(ll i=2,N,r;N=n/i,i<=n;i=r+1)r=n/N,v-=G(N)*(r-i+1);
18     return v;
19 }
20 int F(int d,ll x){
21     d++;int a=0;
22     for(int i=1;pw[i][d]<=x;++i)a+=(d&1&&g[i]-g[i-1]!=1?-1:1)*mu[i]*G(x/pw[i][d]);
23     return a;
24 }
25 int Phi(ll n){
26     if(n<S)return phi[n];
27     int&v=Mphi[n]; if(~v)return v; v=(n&R)*((n&R)+1ll)>>1;
28     for(ll i=2,N,r;N=n/i,i<=n;i=r+1)r=n/N,v-=Phi(N)*(r-i+1);
29     return v;
30 }
31 int main(){
32     mu[1]=phi[1]=g[1]=mt[1]=1;
33     for(int i=2;i<S;++i){
34         if(!np[i])p[++pc]=i,mu[i]=g[i]=-1,phi[i]=i-1,lt[i]=mt[i]=1;
35         for(int j=1,x;(x=i*p[j])<S;++j)
36             if(i%p[j])np[x]=1,mu[x]=-mu[i],phi[x]=phi[i]*(p[j]-1),g[x]=-g[i],mt[x]=mt[i],lt[x]=1;
37             else{np[x]=1;phi[x]=phi[i]*p[j];g[x]=-g[i];mt[x]=max(mt[i],lt[x]=lt[i]+1);break;}
38     }
39     for(int i=2;i<S;++i)phi[i]+=phi[i-1],g[i]+=g[i-1];
40     for(int i=0;i<=100000;++i)pw[i][0]=1;
41     for(int i=0;i<=100000;++i)for(int j=1;j<=41;++j)pw[i][j]=pw[i][j-1]*i;
42     cin>>n>>k;
43     for(int i=1;i<=n&&i<S;++i)if(mt[i]<=k)ans+=(g[i]-g[i-1])*(k+1-mt[i])*(2*Phi(n/i)-1);
44     for(int d=1,lst,z;lst=F(d,S-1),d<=k;++d)for(ll i=S,N,r;N=n/i,i<=n;i=r+1)r=n/N,ans+=((z=F(d,r))-lst)*(2*Phi(N)-1),lst=z;
45     printf("%d",ans&(1<<30)-1);
46 }
View Code

套娃是必须有的,更详细的推导需要去模大神

 

T3:树

大意:树。如果从$a\rightarrow b$,若$a<b$则$val_a++,val_b++$否则$va;_a--,val_b--$。给出最终$val$值,要求最小化有向路径数量满足上述$val$值,此外最小化解的字典序。

即依次最小化$u_1,v_1,u_2,v_2...$。$ n\le 10^6$。保证存在解使路径数$\le n$

首先不考虑字典序来构造一组合法解,那么直接$dfs$然后每次到叶节点再去考虑它和父亲的连边关系即可。

这样可以确定每个点的度数,进而确定每个点作为路径的起点或终点多少次(不可能同时是,否则可以连起来路径数并不最小

然后观察大样例,发现对于两条路径$(a,b)+(c,d)$它们对$val$的贡献。

$(a,b)+(c,d) = (a,b)+(c,d) + (b,d)+(d,b) =(a,b)+(b,d) + (c,b)+(b,d) =(a,d)+(b,c)$

所以发现对于任意一组已知解,我们可以在$val$不变的同时,交换任意两条路径的起点和终点。

那么最小化字典序,就相当于把所有起点所有终点分别排序然后输出就好了。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define S 2222222
 4 int d[S],in[S],out[S],fir[S],l[S],to[S],ec,In,Out,n,deg[S],sum;
 5 void link(int a,int b){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;}
 6 void add(int p,int x,int v){deg[p]+=v;deg[x]-=v;}
 7 void dfs(int p,int fa){
 8     for(int i=fir[p];i;i=l[i])if(to[i]!=fa)dfs(to[i],p);
 9     d[fa]-=d[p];sum+=abs(d[p]);
10     if(p>fa)add(fa,p,d[p]); else add(p,fa,d[p]);
11 }
12 int read(){
13     int p=0,f=0;char ch=getchar();
14     while(!isdigit(ch))f|=ch=='-',ch=getchar(); while(isdigit(ch))p=p*10+ch-48,ch=getchar();
15     return f?-p:p;
16 }
17 int main(){
18     n=read();
19     for(int i=1;i<=n;++i)d[i]=read();
20     for(int i=1,a,b;i<n;++i)a=read(),b=read(),link(a,b),link(b,a);
21     dfs(1,0);
22     for(int i=1;i<=n;++i){
23         while(deg[i]>0)deg[i]--,out[++Out]=i;
24         while(deg[i]<0)deg[i]++,in[++In]=i;
25     }
26     printf("%d\n",In);
27     for(int i=1;i<=In;++i)printf("%d %d\n",out[i],in[i]);
28 }
View Code

 

posted @ 2020-04-24 23:00  DeepinC  阅读(193)  评论(0编辑  收藏  举报