[考试反思]0326省选模拟55:摸索

神仙题系列。

$T1$想的差不多了。但是最后一步垮了。(线段树有个弱智问题想了太久

和正解非常像,但是最后写出来还不如$O(n^2)$的。

$T2$除非写一个特别麻烦的搜索不然都没有分。而搜索也只有$10$分。算了吧

$T3$其实$dp$定义已经想到了,但是时间不够于是没有继续深入。

时间不够,于是在$20$分状压和一种奇怪的乱搞之中选择了乱搞,然后就爆零了。

然后总分就是这么惨淡。在也不相信部分分了。。。

但是其实题目质量还不错($T2$太结论了)有空得去补一补博弈论了。

 

T1:调兵遣将

大意:数列。定义一种合法方案是在整个数列中选出若干子区间,互不相交且所有子区间的$gcd$相同。求每个下标在多少种合法方案中被包含于一个区间内。$n \le 50000,a_i \le 10^9$

首先,正难则反,我们不会求被包含,那么我们就求(总方案-不被任何一个区间包含的方案)。

区间$gcd$极度恶心。但是我们知道对于确定的右端点,$gcd$只有$O(log)$种。

求出所有$gcd$区间之后丢到对应的桶里,现在就只需要考虑对于一种$gcd$其各个区间的关系了。

然后考虑一个$dp$。设$f[i]$表示上一个子串结束位置$\le i$的方案数。

假如我们把上面求$gcd$得到的结果用$(l_1,l_2,r)$表示。那么按照$r$排序之后依次加入,转移是:

$f[r...n] += \sum\limits_{L=l_1}^{l_2} f[L-1]$。就是说,依次加入$[l_1,r],[l_1+1,r],...[l_2,r]$这些子串,贡献是$f[L-1]$。而右端点是$r$

同理求出一个$g$表示倒着加入,上一个子串左端点$\ge i$的方案数。

$f[n]$即为总方案数。对于每个点$i$,不包含它的方案数就是$f[i-1]\times g[i+1]$

想到这里你就有$30$分了。开心吧?

然而我们发现,我们把每个区间的固定点(右端点固定时左端点是个区间,反之亦然)都拿出来排序,然后相邻两个关键点之间的部分,它们的$f,g$均相同,故$f\times g$也相同。

所以只用对于每个间隙查询。时间复杂度为$O(nlog^2n)$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define S 111111
 4 #define mod 998244353
 5 map<int,int>M;
 6 vector<int>L1[S<<3],L2[S<<3],R[S<<3],L[S<<3],R1[S<<3],R2[S<<3];
 7 int n,w[S],q[S],v[S],rq[S],rv[S],cnt,tot,pts[S],ptc;
 8 int gcd(int a,int b){return b?gcd(b,a%b):a;}
 9 int mo(int x){return x>=mod?x-mod:x;}
10 struct Tree{
11     int tv[S<<2],lz[S<<2];
12     #define lc p<<1
13     #define rc lc|1
14     #define md (cl+cr>>1)
15     void clear(int p=1,int cl=0,int cr=n+1){
16         if(!tv[p])return;
17         tv[p]=lz[p]=0;
18         if(cl!=cr)clear(lc,cl,md),clear(rc,md+1,cr);
19     }
20     void add(int w,int l,int r,int p=1,int cl=0,int cr=n+1){
21         if(l<=cl&&cr<=r){lz[p]=mo(lz[p]+w);tv[p]=(tv[p]+w*(cr-cl+1ll))%mod;return;}
22         if(lz[p])lz[lc]=mo(lz[lc]+lz[p]),lz[rc]=mo(lz[rc]+lz[p]),tv[lc]=(tv[lc]+(md-cl+1ll)*lz[p])%mod,tv[rc]=(tv[rc]+1ll*(cr-md)*lz[p])%mod,lz[p]=0;
23         if(l<=md)add(w,l,r,lc,cl,md); if(r>md)add(w,l,r,rc,md+1,cr);
24         tv[p]=mo(tv[lc]+tv[rc]);
25     }
26     int ask(int l,int r,int p=1,int cl=0,int cr=n+1){
27         if(l<=cl&&cr<=r)return tv[p];
28         if(lz[p])lz[lc]=mo(lz[lc]+lz[p]),lz[rc]=mo(lz[rc]+lz[p]),tv[lc]=(tv[lc]+(md-cl+1ll)*lz[p])%mod,tv[rc]=(tv[rc]+1ll*(cr-md)*lz[p])%mod,lz[p]=0;
29         return mo((l<=md?ask(l,r,lc,cl,md):0)+(r>md?ask(l,r,rc,md+1,cr):0));
30     }
31 }ll,rr,ans;
32 int main(){//freopen("ex_A2.in","r",stdin);
33     cin>>n;
34     for(int i=1;i<=n;++i)scanf("%d",&w[i]);
35     for(int i=1,t=0,rt;i<=n;++i){
36         q[++t]=i;v[t]=w[i];
37         for(int j=1;j<t;++j)v[j]=gcd(v[j],w[i]); rt=v[t+1]=0;
38         for(int j=1;j<=t;++j)if(v[j]!=v[j+1])rq[++rt]=q[j],rv[rt]=v[j];
39         for(int j=1;j<=rt;++j){
40             q[j]=rq[j],v[j]=rv[j]; int o;
41             o=M[v[j]];if(!o)M[v[j]]=o=++cnt;
42             L1[o].push_back(q[j-1]+1); L2[o].push_back(q[j]); R[o].push_back(i);
43         }t=rt;
44     }q[0]=n+1;
45     for(int i=n,t=0,rt;i;--i){
46         q[++t]=i;v[t]=w[i];
47         for(int j=1;j<t;++j)v[j]=gcd(v[j],w[i]); rt=v[t+1]=0;
48         for(int j=1;j<=t;++j)if(v[j]!=v[j+1])rq[++rt]=q[j],rv[rt]=v[j];
49         for(int j=1;j<=rt;++j){
50             q[j]=rq[j],v[j]=rv[j]; int o;
51             o=M[v[j]];if(!o)M[v[j]]=o=++cnt;
52             R2[o].push_back(q[j-1]-1); R1[o].push_back(q[j]); L[o].push_back(i);
53         }t=rt;
54     }
55     for(int i=1;i<=cnt;++i){
56         ptc=2;pts[1]=1;pts[2]=n;
57         rr.clear(); rr.add(1,0,n+1);
58         for(int j=0;j<R[i].size();++j)rr.add(rr.ask(L1[i][j],L2[i][j]),R[i][j]+1,n+1),pts[++ptc]=R[i][j];
59         tot=mo(tot+rr.ask(n+1,n+1));
60         ll.clear(); ll.add(1,0,n+1);
61         for(int j=0;j<L[i].size();++j)ll.add(ll.ask(R1[i][j],R2[i][j]),0,L[i][j]-1),pts[++ptc]=L[i][j];
62         sort(pts+1,pts+1+ptc);
63         for(int j=1,p;j<=ptc;++j)if(pts[j]!=pts[j-1]){
64             p=pts[j];ans.add(1ll*ll.ask(p,p)*rr.ask(p,p)%mod,p,p);
65             if(pts[j]>pts[j-1]+1)p=pts[j-1]+1,ans.add(1ll*ll.ask(p,p)*rr.ask(p,p)%mod,p,pts[j]-1);
66         }
67     }for(int i=1;i<=n;++i)printf("%d ",mo(tot+mod-ans.ask(i,i)));//cerr<<endl<<cnt<<endl;
68 }
View Code

 

T2:一掷千金

大意:树。每个点编号为二维坐标$(x,y)$。其父亲为$(max(x-1,0),max(y-1,0))$。$(0,0)$为根。

最开始的时候$n$个矩阵的点都是亮的。每次操作可以选定一个亮的点,使祖先链全部点亮暗状态改变。不能操作者败。求$SG$值。

$n,x \le 10^5,y\le 10^9$

挺经典的翻硬币模型,所以满足:对于整个局面中所有亮的点,可以把它孤立出来,把它和祖先链单独作为一场游戏考虑$SG$值。(祖先链都是暗的)

然后对于所有的初始亮点,我们把所有亮点的SG值异或起来,就是总局面的$SG$值。

上述结论均可用归纳法大致证明(看的不太明白)。

附个为数不多的带证明的链接

翻动最后一个节点的状态一定会使前面若干节点的状态取反,而答案是异或起来的,所以出现两次就抵消,符合条件。

然后还可以(打表)发现,对于一个点$(x,y)$,它$SG$值就是$lowbit(max(x,y))$

所以,至此,问题转化为每个点的权值为$lowbit(max(x,y))$。求矩阵并的权值异或和。

矩阵面积的套路:扫描线+线段树。坐标过大所以要动态开点。

而$lowbit$这种操作是二进制运算,为了方便我们可以把值域扩大至$[0,2^30)$再建线段树。

这样,线段树的区间都类似与$[aL,(a+1)L-1]$。

这样的区间的$lowbit$异或和:发现对于$lowbit<\frac{L}{2}$的部分,一定两两抵消,所以最终的$lowbit$异或和为$lowbit(l) \ xor \ lowbit(mid)$

然后只要标记永久化表示当前区间被完全覆盖多少次,如果被覆盖则$O(1)$计算否则由儿子上传即可。时间复杂度$O(nlogn)$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int S=111111,N=(1<<30)-1;
 4 int sum[S<<8],all[S<<8],cnt[S<<8],lc[S<<8],rc[S<<8],pc,ans,R,q,n,m;
 5 vector<int>op[S],l[S],r[S];
 6 int cal(int l,int r){return l&-l^r-l+1>>1;}
 7 void up(int p){cnt[p]=cnt[lc[p]]^cnt[rc[p]];sum[p]=sum[lc[p]]^sum[rc[p]];}
 8 #define md (L+R>>1)
 9 void chg(int&p,int l,int r,int v,int L=0,int R=N){
10     if(!p)p=++pc;
11     if(l<=L&&R<=r){all[p]+=v; if(all[p])cnt[p]=R^L^1,sum[p]=cal(L,R);else up(p); return;}
12     if(l<=md)chg(lc[p],l,r,v,L,md); if(r>md)chg(rc[p],l,r,v,md+1,R); if(!all[p])up(p);
13 }
14 int CNT(int p,int l,int r,int L=0,int R=N){
15     if(!p)return 0; if(all[p])return min(R,r)^max(L,l)^1; if(l<=L&&R<=r)return cnt[p];
16     return (l<=md?CNT(lc[p],l,r,L,md):0)^(r>md?CNT(rc[p],l,r,md+1,R):0);
17 }
18 int SUM(int p,int l,int r,int L=0,int R=N,int o=0){
19     o|=all[p]; if(l<=L&&R<=r)return o?cal(L,R):sum[p];
20     return (l<=md?SUM(lc[p],l,r,L,md,o):0)^(r>md?SUM(rc[p],l,r,md+1,R,o):0);
21 }
22 int main(){
23     cin>>q>>n>>m;
24     #define pb push_back
25     for(int i=1,x,y,X,Y;i<=q;++i)
26         scanf("%d%d%d%d",&x,&y,&X,&Y),X++,
27         op[x].pb(1),l[x].pb(y),r[x].pb(Y),
28         op[X].pb(-1),l[X].pb(y),r[X].pb(Y);
29     for(int i=1;i<=n;++i){
30         for(int j=0;j<l[i].size();++j)chg(R,l[i][j],r[i][j],op[i][j]);
31         ans^=SUM(R,i+1,N)^(CNT(R,0,i)&1)*(i&-i);
32     }cout<<ans<<endl;
33 }
View Code

 

T3:树拓扑序

大意:给定外向树,求所有拓扑序列中,逆序对个数和。$n \le 500$

设$dp[i]$表示$i$的子树的拓扑序逆序对个数,$tp[i]$表示子树拓扑序数,$f[i][j]$表示数字$i$在目前考虑的子树中排名第$j$的方案数。

枚举两棵子树考虑其贡献,那就是,枚举其中一棵树中的某一个权值及其排名,再枚举$bef$它在另一个子树中有多少数在他前面。

$dp[p]+= size(p)! \sum\limits_{a\in son(p)} \  \sum\limits_{b \in son(p),b \neq a} \frac{1}{(size(a)+size(b))!} \prod\limits_{c \in son(p),c \neq a,c \neq b} \frac{tp[c]}{size(c)!}  \sum\limits_{val\in subtree(a)} \sum\limits_{rk=1}^{size(a)} f[val][rk] \sum\limits_{bef=0}^{size(b)}\binom{bef+rk-1}{bef}  \binom{size(a)-rk+size(b)-bef}{size(b)-bef} (\sum\limits_{VAL \in subtree(b)} \sum\limits_{RK=1}^{size(b)} [(VAL>val \ and \ RK\le bef)or(VAL<val \ and \ RK<bef)] f[VAL][RK] )$

最后一段可以用一个前缀和直接算出来,然后考虑一下前面的复杂度:

看起来是$5$层循环,然而做前缀和时,枚举两个值这里,因为每两个值只会在$lca$处被枚举到一次,再枚举一下排名,一共是$O(n^3)$

然后转移的时候,枚举权值的总复杂度是$O(n)$的,然后接下来枚举的是两个子树的$size$。对于全图求和之后也是$O(n^2)$级别的。

然后更新$dp$也是同理,所以总复杂度是$O(n^3)$的。

转移还有$dp[p]+=dp[son] \times tp[otherson] \times \binom{size(p)-1}{size(son)}$

以及更新$dp$的时候,只需要枚举一个儿子,和当前已经合并出来的整棵树归并。

总时间复杂度$O(n^3)$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define S 505
 4 #define mod 1000000007
 5 vector<int>v[S],s[S];
 6 int C[S][S],ec,n,sz[S],ans[S],dp[S][S],tp[S],tot[S];
 7 int mo(int a){return a>=mod?a-mod:a;}
 8 void dfs(int p){
 9     sz[p]=tp[p]=1; v[p].push_back(p);
10     for(int y:s[p]){
11         dfs(y);    sz[p]+=sz[y]; tp[p]=1ll*tp[p]*tp[y]%mod*C[sz[p]-1][sz[y]]%mod;
12         for(int x:v[y])v[p].push_back(x);
13     }
14     for(int y:s[p])for(int z:s[p])if(z^y){
15         int c=0,r=1,a=0;
16         for(int w:s[p])if(w!=y&&w!=z)c+=sz[w],r=1ll*tp[w]*r%mod*C[c][sz[w]]%mod;
17         for(int w:v[y]){
18             for(int i=1;i<=sz[z];++i)tot[i]=0;
19             for(int q:v[z])if(q<w)for(int i=1;i<=sz[z];++i)tot[i]=mo(tot[i]+dp[q][i]);
20             for(int i=1;i<=sz[z];++i)tot[i]=mo(tot[i]+tot[i-1]);
21             for(int i=1;i<=sz[y];++i)for(int j=1;j<=sz[z];++j)
22                 a=(a+1ll*dp[w][i]*tot[j]%mod*C[i+j-1][j]%mod*C[sz[y]-i+sz[z]-j][sz[y]-i])%mod;
23         }ans[p]=(ans[p]+1ll*r*a%mod*C[sz[p]-1][c])%mod;
24     }dp[p][1]=tp[p];
25     for(int y:s[p]){
26         int c=0,r=1;
27         for(int z:s[p])if(y^z)c+=sz[z],r=1ll*r*tp[z]%mod*C[c][sz[z]]%mod;
28         ans[p]=(ans[p]+1ll*r*ans[y]%mod*C[sz[p]-1][c])%mod;
29         for(int w:v[y]){
30             for(int i=1;i<=sz[p];++i)tot[i]=0;
31             for(int i=1;i<=sz[y];++i)for(int j=0;j<=c;++j)
32                 tot[i+j]=(tot[i+j]+1ll*dp[w][i]*C[i+j-1][j]%mod*C[sz[y]-i+c-j][c-j])%mod;
33             for(int i=1;i<=sz[p];++i)dp[w][i]=1ll*tot[i-1]*r%mod;
34         }
35     }
36 }
37 int main(){
38     cin>>n;
39     for(int i=1,a,b;i<n;++i)cin>>a>>b,s[a].push_back(b);
40     for(int i=0;i<=n;++i)for(int j=C[i][0]=1;j<=i;++j)C[i][j]=mo(C[i-1][j-1]+C[i-1][j]);
41     dfs(1);
42     for(int i=1;i<=n;++i)for(int j:v[i])if(i<j)ans[1]=mo(ans[1]+tp[1]);
43     cout<<ans[1];
44 }
View Code

 

posted @ 2020-03-27 17:43  DeepinC  阅读(204)  评论(0编辑  收藏  举报