• [考试反思]0320省选模拟50:祖宗


    数据强横如斯,我人没了。。。

    $T1$没开够$long long$一分不剩。(算的时候用了,输出的时候忘了)

    快乐。而且点分治还写挂了。改成$long long$也会$T40$。

    不过,这是第一次考场上写点分治,差不多算是写出来了,可喜可贺(虽然还是锅了

    $T2$写了个线段树,写的有点像$ODT$(也不像),在随机数据下大概是两个$log$的,然而数据很不讲道理,蹭了个下线$20$就走了

    T3全场最高也就10分这就没啥好说的。

    不开$long long$真的会见祖宗啊,出数据的还会让你一分都没有的见祖宗啊。。。

    长记性。。。但愿吧。。。

    T1:小A的树

    大意:树,输出前$k$长的树上路径。$n,k le 2 imes 10^5$

    树上路径——点分治啊。

    极其草率的路径合并,用上《淘金》或者《超级钢琴》的逐步扩展的套路复杂度就对了。

    记录每条路径的来源,同一子树的要跳过。

    然后俩优先队列各干各的活就行了。一些明显的最优性剪枝写一写,跑的还挺快的。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define S 200005
     4 #define ll long long
     5 #define For() for(int i=fir[p];i;i=l[i])if(to[i]!=fa&&!al[to[i]]) 
     6 priority_queue<ll,vector<ll>,greater<ll> >q;
     7 priority_queue<pair<ll,int> >Q;
     8 int C,tsz,bst,sz[S],n,k,t,fir[S],to[S<<1],v[S<<1],l[S<<1],ec,K,pt[S],nxt[S];
     9 bool al[S]; pair<ll,int>a[S];ll r[S],qtop;
    10 void link(int a,int b,int w){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;v[ec]=w;}
    11 void Cent(int p,int fa=0){
    12     int msz=0;sz[p]=1;
    13     For()Cent(to[i],p),sz[p]+=sz[to[i]],msz=max(msz,sz[to[i]]);
    14     msz=max(msz,tsz-sz[p]); if(msz<bst)C=p,bst=msz;
    15 }
    16 void dfs(int p,int fa,ll d,int o){
    17     a[++t]=make_pair(d,o);
    18     For()dfs(to[i],p,d+v[i],o);
    19 }
    20 void DaC(int p,int fa=0){
    21     t=0; al[p]=1;
    22     For()dfs(to[i],0,v[i],i);
    23     a[++t]=make_pair(0,0);
    24     sort(a+1,a+1+t); reverse(a+1,a+1+t);
    25     a[t+1].second=-2;
    26     for(int i=1;i<=t;++i){
    27         nxt[i]=i+1;
    28         while(a[nxt[i]].second==a[i].second)nxt[i]++;
    29     }
    30     if(a[1].first*2<=qtop)return;
    31     for(int i=1;i<t;++i)if(a[i].first+a[nxt[i]].first>qtop)Q.push(make_pair(a[i].first+a[pt[i]=nxt[i]].first,i));
    32     while(Q.size()&&Q.top().first>qtop){
    33         ll x=Q.top().first;int id=Q.top().second;Q.pop();
    34         q.pop(),q.push(x);qtop=q.top();
    35         pt[id]++;if(a[pt[id]].second==a[id].second)pt[id]=nxt[pt[id]];
    36         if(pt[id]!=t+1&&a[id].first+a[pt[id]].first>qtop)Q.push(make_pair(a[id].first+a[pt[id]].first,id));
    37     }while(Q.size())Q.pop();
    38     xxx:For()tsz=bst=sz[to[i]],Cent(to[i]),DaC(C);
    39 }
    40 int read(){
    41     register int p=0;register char ch=getchar();
    42     while(!isdigit(ch))ch=getchar();
    43     while(isdigit(ch))p=(p<<3)+(p<<1)+ch-'0',ch=getchar();
    44     return p;
    45 }
    46 int main(){//freopen("tree20.in","r",stdin);freopen("my.out","w",stdout);
    47     cin>>n>>K;
    48     for(int i=1,a,b,w;i<n;++i)a=read(),b=read(),w=read(),link(a,b,w),link(b,a,w);
    49     for(int i=1;i<=K;++i)q.push(0);
    50     tsz=bst=n;Cent(1);DaC(C); int z=0;
    51     while(q.size())r[++z]=q.top(),q.pop(); while(z)printf("%lld
    ",r[z]),z--;
    52 }
    View Code

    T2:小B的序列

    大意:序列,支持区间按位与/或上某个数,查询区间最大值。$n ,q le 2 imes 10^5$

    哭了,福建人干什么了就要被吃了

    区间与和区间或,这个操作看起来好像会使序列趋于统一,所以好像大概是个复杂度正确的乱搞。

    小清新线段树啊。

    好像做过多数的暴力线段树好像都是维护$min,max$,然后如果$min,max$差值不大就可以直接不递归了。

    然而这道题是位运算,所以维护$min,max$是不合适的,我们维护$or,and$。

    发现如果对于一次修改,整个区间内的$and,or$对于这个修改的变化量是相同的(减少或增加的量),那么就可以直接区间加了。

    所以就这么暴力。没了。

    时间复杂度的话,不会势能分析,大概口胡。

    大概可以理解为:对于一次区间与操作,我们考虑它的所有0位。

    它操作区间内所有数的这一位就都变成了0,也就是都一样了,那么也就是这段区间上这一位的势能直接被清空了。

    同时在两个端点处,所在的所有区间的这一位的势能可能会增加,最多是$2log$点。

    而对于所有的1位,这一位上不会产生任何变化,不会因为这一位而递归下去,也不会产生势能变化。

    所以每次其实只是收获了已充能的0位上的势能,并且在最多$log$位上各充入$2log$势能。

    最开始所有位上的势能大概也就是$nlog^2n$的所以总复杂度就是$O(nlog^2n)$

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define S 200005
     4 int n,a[S],q,mx[S<<2],lz[S<<2],And[S<<2],Or[S<<2];
     5 #define lc p<<1
     6 #define rc p<<1|1
     7 #define md (cl+cr>>1)
     8 void up(int p){mx[p]=max(mx[lc],mx[rc]);And[p]=And[lc]&And[rc];Or[p]=Or[lc]|Or[rc];}
     9 void down(int p){lz[lc]+=lz[p];lz[rc]+=lz[p];mx[lc]+=lz[p];mx[rc]+=lz[p];And[lc]+=lz[p];And[rc]+=lz[p];Or[lc]+=lz[p];Or[rc]+=lz[p];lz[p]=0;}
    10 void build(int p=1,int cl=1,int cr=n){
    11     if(cl==cr){mx[p]=And[p]=Or[p]=a[cl];return;}
    12     build(lc,cl,md); build(rc,md+1,cr); up(p);
    13 }
    14 void OR(int l,int r,int w,int p=1,int cl=1,int cr=n){
    15     if(l<=cl&&cr<=r&&(w|Or[p])-Or[p]==(w|And[p])-And[p]){
    16         int d=(w|Or[p])-Or[p];
    17         lz[p]+=d;Or[p]+=d;And[p]+=d;mx[p]+=d;
    18         return;
    19     }
    20     if(lz[p])down(p);
    21     if(l<=md)OR(l,r,w,lc,cl,md); if(r>md)OR(l,r,w,rc,md+1,cr); up(p);
    22 }
    23 void AND(int l,int r,int w,int p=1,int cl=1,int cr=n){
    24     if(l<=cl&&cr<=r&&(w&Or[p])-Or[p]==(w&And[p])-And[p]){
    25         int d=(w&Or[p])-Or[p];
    26         lz[p]+=d;Or[p]+=d;And[p]+=d;mx[p]+=d;
    27         return;
    28     }
    29     if(lz[p])down(p);
    30     if(l<=md)AND(l,r,w,lc,cl,md); if(r>md)AND(l,r,w,rc,md+1,cr); up(p);
    31 }
    32 int ask(int l,int r,int p=1,int cl=1,int cr=n){
    33     if(l<=cl&&cr<=r)return mx[p];
    34     if(lz[p])down(p);
    35     return max(l<=md?ask(l,r,lc,cl,md):0,r>md?ask(l,r,rc,md+1,cr):0);
    36 }
    37 int main(){
    38     cin>>n>>q;
    39     for(int i=1;i<=n;++i)scanf("%d",a+i);
    40     build();
    41     for(int o,x,y,w;q--;){
    42         scanf("%d%d%d",&o,&x,&y);
    43         if(o==3)printf("%d
    ",ask(x,y));
    44         else if(o==1)scanf("%d",&w),AND(x,y,w);
    45         else scanf("%d",&w),OR(x,y,w);
    46     }
    47 }
    View Code

    T3:小C的利是

    大意:$n imes n$矩阵,每行每列选一个数共$n$个。不能选$-1$。问选出的位置的和是否有可能是$k$的倍数。$n,k le 100$

    发现这个定义和行列式的定义非常像。所以我们考虑用行列式来解。

    然而行列式是乘法这题是加法,所以不难想到原根。

    然而原根不是很好处理,在这里我们使用$k$次单位根会方便不少。设为$g$.

    设$S$表示

    既然和$S$是k的倍数,那么大概能想到$g^S equiv 1$。

    于是我们找一个大质数$P=ak+1$。有$g^{ak} equiv 1 (mod P)$

    这样,我们构造一种权值,使得是否存在K的倍数,在行列式中得以区分。

    怎么让是1的值有贡献,而不是1的值没有贡献呢?

    想起数学老师们再三强调的,等比数列的公比是1时不能直接套公式。

    如果我们能让非1的排列产生的贡献是$1+x^S+x^{2S}+x^{3S}+...+x^{(k-1){S}} = frac{1-x^{kS}}{1-x^S} equiv 0 (mod P)$

    然后是1的排列随便产生点什么非$0$的贡献就好了。

    既然上式是加法,于是我们可以干脆做多次行列式,第一次贡献$1$,第二次$x^S$。。。就好了

    所以我们可以发现,对于一个$A_{i,j}=-1$那么直接在行列式矩阵里把它弄成$0$,否则弄成$x^{A_{i,j}}$

    其中$x$在$k$轮中分别取$g^0,g^1...g^{k-1}$.这样就符合了上述的条件,同时在取到$1$的排列的时候贡献是$k$而不是0。

    (这怎么能想到啊

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int n,k,a[111][111],P,g,r[111][111],b[111][111],det,x;
     4 bool isp(int p){
     5     for(int i=2;i*i<=p;++i)if(p%i==0)return 0;
     6     return 1;
     7 }
     8 int qp(int b,int t,int a=1){if(t==-1)return 0;for(;t;t>>=1,b=1ll*b*b%P)if(t&1)a=1ll*a*b%P;return a;}
     9 int Gauss(int ret=1){
    10     for(int i=1;i<=n;++i){
    11         if(!b[i][i])for(int k=i+1;k<=n;++k)if(b[k][i]){swap(b[i],b[k]);ret=P-ret;break;}
    12         ret=1ll*ret*b[i][i]%P;
    13         for(int j=n,iv=qp(b[i][i],P-2);j>=i;--j)b[i][j]=1ll*b[i][j]*iv%P;
    14         for(int j=i+1;j<=n;++j)for(int k=n;k>=i;--k)b[j][k]=(b[j][k]-1ll*b[j][i]*b[i][k]%P+P)%P;
    15     }return ret;
    16 }
    17 int main(){
    18     cin>>n>>k;
    19     for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)scanf("%d",&a[i][j]);
    20     P=k*317913+1;
    21     while(!isp(P))P+=k;
    22     for(g=2;g<P;++g)if(qp(g,k)==1)break;
    23     for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)r[i][j]=rand()%P;
    24     for(int _=0,x=1;_<k;++_,x=1ll*x*g%P){
    25         for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)b[i][j]=1ll*qp(x,a[i][j])*r[i][j]%P;
    26         det=(det+Gauss())%P;
    27     }puts(det%P?"Yes":"No");
    28 }
    View Code
  • 相关阅读:
    VM player无法联网问题
    寄存器
    linux下的文件操作
    linux的切换目录操作
    linux的ls -al指令
    python对ASC码的加减
    ASC码速记
    pyhton的返回值
    intellij 调试方法
    2015,5.10
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12535136.html
Copyright © 2020-2023  润新知