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

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

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

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

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

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

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

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

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

 

T1:小A的树

大意:树,输出前$k$长的树上路径。$n,k \le 2 \times 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\n",r[z]),z--;
52 }
View Code

 

T2:小B的序列

大意:序列,支持区间按位与/或上某个数,查询区间最大值。$n ,q \le 2 \times 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\n",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 \times 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

 

posted @ 2020-03-20 21:47  DeepinC  阅读(...)  评论(...编辑  收藏