zoukankan      html  css  js  c++  java
  • [考试反思]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] += sumlimits_{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] imes g[i+1]$

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

    然而我们发现,我们把每个区间的固定点(右端点固定时左端点是个区间,反之亦然)都拿出来排序,然后相邻两个关键点之间的部分,它们的$f,g$均相同,故$f imes 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,yle 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)! sumlimits_{ain son(p)}   sumlimits_{b in son(p),b eq a} frac{1}{(size(a)+size(b))!} prodlimits_{c in son(p),c eq a,c eq b} frac{tp[c]}{size(c)!}  sumlimits_{valin subtree(a)} sumlimits_{rk=1}^{size(a)} f[val][rk] sumlimits_{bef=0}^{size(b)}inom{bef+rk-1}{bef}  inom{size(a)-rk+size(b)-bef}{size(b)-bef} (sumlimits_{VAL in subtree(b)} sumlimits_{RK=1}^{size(b)} [(VAL>val and RKle 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] imes tp[otherson] imes inom{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
  • 相关阅读:
    《P3953 [NOIP2017 提高组] 逛公园》
    《P4180 [BJWC2010]严格次小生成树》
    《济南icpc补题》
    《levil的因子和》
    《洛谷P2704 [NOI2001]炮兵阵地》
    《Codeforces Round #689 (Div. 2, based on Zed Code Competition)》
    《2174: Leapin' Lizards》
    《3820: Revenge of Fibonacci 》
    马拉车求最长回文子串
    二分训练
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12582848.html
Copyright © 2011-2022 走看看