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
  • 相关阅读:
    第十二章学习笔记
    UVa OJ 107 The Cat in the Hat (戴帽子的猫)
    UVa OJ 123 Searching Quickly (快速查找)
    UVa OJ 119 Greedy Gift Givers (贪婪的送礼者)
    UVa OJ 113 Power of Cryptography (密文的乘方)
    UVa OJ 112 Tree Summing (树的求和)
    UVa OJ 641 Do the Untwist (解密工作)
    UVa OJ 105 The Skyline Problem (地平线问题)
    UVa OJ 100 The 3n + 1 problem (3n + 1问题)
    UVa OJ 121 Pipe Fitters (装管子)
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12582848.html
Copyright © 2011-2022 走看看