[考试反思]0203省选模拟17:庸碌

又有原题。所以还是4.5h4道题。

0+60+15+4=79。rk15。

考了个x啊这是。

这次出咕咕咕估分功能了(真的咕了好久啊)

估分是这样的:0+60+50+100=210

T4是原题。

当时我乱搞AC了。考后又打了一遍正解又A了一遍。

于是上报了,教练说不打正解的直接给0分。

然而KDtree是不是正解我不知道,复杂度高但是可以过。反正我也不会,我的乱搞被卡了而已。

正解思路还记得挺清晰。但是实现的时候忘赋初值了。。。

诶不是现在的样例怎么都是可以乱过的啊。。。

实际数据下只过了n=1。。。。

估分100实际4。。。真。。。

%%%miku又一次写了KDtree,然后剪枝后精确估分92

省选重题好多啊。。。就题面改了一点,连数据都没换。。。

T3是原题强化。数据都出满了所以估50肯定是高了,但是没有锅的话应该是30

然而15分。。。

第一次尝试用了一下NC极力推荐的加减函数,然后把加减写反了。。。

是MT的加边。我那么一弄变成边加点减了。。。

然后很奇妙,对于n是偶数的测试点我的答案都负了过去。

测试点保证n=5/6/7/8/9/10各一个点,所以我精确的拿到3个有15分。。。

幸亏这个暴力数据没出满不然就真爆零了。。。

后两题这么一折腾就剩下70分钟左右。。。看着T2的部分分多就直接上来就打

稳稳的打到60再高的也没想。。。然后惊奇的成为4题里分最高的一个

最后给T1剩下了40分钟。想到了正解(虽然麻烦但是可以AC的)剩下的时间也就二十多分钟了。

暴写,将近3k。写完,过编译,调RE,然后开始弄样例。。。

时间还是没赶上。最后交个没过样例的上去信仰跑自觉估分0

其实没差多少了就有一个细节忘写了

然后就垫底了。。。

改题还是很顺利。。。毕竟考场上的思路都差不太多。。。

只不过T2突然被xm问住发现自己少证了,不过后来又被miku教会了。

顺便把咕咕咕的昨天T3给弄掉了,cin被卡常了23333

T1:选择

大意:图,支持删边,查询两点是否在一个环内。$n,m,q le 100000$

删边?

时光倒流。

环,加边?

像生成树。

链修改,链查询?

树剖,线段树。

维护在一个环内?

并查集。

然后这题就没了。但是代码还是很麻烦。

仔细想一想不难发现线段树其实是弱智行为,直接记录并查集顶端的点然后暴跳。

在同一个并查集上的点在树上当然是一个联通块。。。

暴跳复杂度少个log,常数也小,代码也好写。。。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define S 666666
 4 int n,m,q,dt[S],f[S],dfn[S],tim,top[S],fir[S],l[S],to[S],ec,sz[S],hson[S],w[S],idfn[S];
 5 int dep[S],U[S],V[S],te[S],tf[S],ans[S];char o[S][2];
 6 unordered_map<int,int>M[S];unordered_set<int>s,rs;
 7 struct ed{
 8     int x,y,t,o;
 9     friend bool operator<(ed a,ed b){return a.t>b.t;}
10 }E[S];
11 void link(int a,int b){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;}
12 void dfs(int p,int F){
13     sz[p]=1;f[p]=F;dep[p]=dep[F]+1;
14     for(int i=fir[p];i;i=l[i])if(to[i]!=F){
15         dfs(to[i],p);sz[p]+=sz[to[i]];
16         if(sz[to[i]]>sz[hson[p]])hson[p]=to[i];
17     }
18 }
19 void DFS(int p,int tp){
20     dfn[p]=++tim;top[p]=tp;idfn[tim]=p;
21     if(hson[p])DFS(hson[p],tp);
22     for(int i=fir[p];i;i=l[i])if(to[i]!=f[p]&&to[i]!=hson[p])DFS(to[i],to[i]);
23 }
24 #define lc p<<1
25 #define rc lc|1
26 #define md (cl+cr>>1)
27 void build(int p,int cl,int cr){
28     if(cl==cr){w[p]=idfn[cl];return;}
29     build(lc,cl,md);build(rc,md+1,cr);
30 }
31 void chg(int l,int r,int v,int p=1,int cl=1,int cr=n){
32     if(l<=cl&&cr<=r){w[p]=v;return;}
33     if(l<=md)chg(l,r,v,lc,cl,md);
34     if(r>md)chg(l,r,v,rc,md+1,cr);
35     w[p]=w[lc]==w[rc]?w[lc]:0;
36 }
37 void ask(int l,int r,int p=1,int cl=1,int cr=n){
38     if(w[p]){s.insert(w[p]);return;}
39     if(l<=md)ask(l,r,lc,cl,md);
40     if(r>md)ask(l,r,rc,md+1,cr);
41 }
42 void upd(int a,int b,int v){
43     if(a==b){chg(dfn[a],dfn[a],v);return;}
44     while(top[a]!=top[b])if(dep[top[a]]<dep[top[b]])chg(dfn[top[b]],dfn[b],v),b=f[top[b]];
45         else chg(dfn[top[a]],dfn[a],v),a=f[top[a]];
46     if(dep[a]>dep[b])chg(dfn[b],dfn[a],v); else chg(dfn[a],dfn[b],v);
47 }
48 void query(int a,int b){s.clear();
49     while(top[a]!=top[b])if(dep[top[a]]<dep[top[b]])ask(dfn[top[b]],dfn[b]),b=f[top[b]];
50         else ask(dfn[top[a]],dfn[a]),a=f[top[a]];
51     if(dep[a]>dep[b])ask(dfn[b],dfn[a]); else ask(dfn[a],dfn[b]);
52 }
53 int find(int p){return tf[p]==p?p:tf[p]=find(tf[p]);}
54 int Find(int p){
55     query(p,p);int x=*s.begin();
56     if(x!=p)x=Find(x),upd(p,p,x);
57     return x;
58 }
59 int main(){
60     scanf("%d%d%d",&n,&m,&q);
61     for(int i=1;i<=m;++i)scanf("%d%d",&E[i].x,&E[i].y),M[E[i].x][E[i].y]=M[E[i].y][E[i].x]=E[i].o=i;
62     for(int i=1;i<=q;++i)scanf("%s%d%d",o[i],&U[i],&V[i]);
63     for(int i=1;i<=q;++i)if(o[i][0]=='Z')E[M[U[i]][V[i]]].t=i;
64     for(int i=1;i<=m;++i)if(!E[i].t)E[i].t=q+1;
65     sort(E+1,E+1+m);
66     for(int i=1;i<=n;++i)tf[i]=i;
67     for(int i=1;i<=m;++i)if(find(E[i].x)!=find(E[i].y))tf[tf[E[i].x]]=tf[E[i].y],te[E[i].o]=1,link(E[i].x,E[i].y),link(E[i].y,E[i].x);
68     for(int i=1;i<=n;++i)if(find(i)!=find(1)&&i==find(i))link(1,i);
69     dfs(1,0);DFS(1,1);build(1,1,n);
70     for(int i=1;i<=m;++i)if(!te[E[i].o]&&E[i].t==q+1){
71         int F=Find(E[i].x);
72         query(E[i].x,E[i].y);
73         rs.clear();swap(s,rs);
74         for(auto x:rs)upd(Find(x),Find(x),F);
75     }
76     for(int i=q;i;--i)if(o[i][0]=='P')ans[i]=Find(U[i])==Find(V[i]);
77         else if(!te[M[U[i]][V[i]]]){
78             int F=Find(U[i]);
79             query(U[i],V[i]);
80             rs.clear();swap(s,rs);
81             for(auto x:rs){
82                 int p=Find(x);
83                 if(p!=F)upd(p,p,F);
84             }
85         }
86     for(int i=1;i<=q;++i)if(o[i][0]=='P')puts(ans[i]?"Yes":"No");
87 }
View Code

T2:划分序列

大意:将数列分为k段,使最大段的和最小。$k le n le 50000,|A_i| le 30000$

这个限制条件看起来真是像二分。

如果权值都是正的,可以二分。

如果权值都是负的,可以二分。

就算不满足这些,还是可以二分。

二分之后我们看能把这个数列至多/少各能分成多少段,如果k在这个范围内那么就合法。

感性理解很容易,但是要证明还是有点小恶心。但还是可以证的。

我们要证明的就是若可以分成$l,r$段就一定可以分成$i(l<i<r)$段。

对于第一个前缀合法的点一定只能分成一段对吧
然后对于第二个,要不只能分成一段,要不只能分成两段
分成一段没有意义,我们假设他只能分成两段
也就是说sum2>mid
然后看第三个点
假设他能分成3段也能分成一段
那么他一定可以分成两段
因为如果不能
则sum3-sum1>mid,sum2>mid
那么sum3>mid
不能分成一段
依次类推
应该就差不多了趴
——By %%mikufun

类似归纳的方法(其实就是归纳)。没啥毛病。所以就可以心安理得的AC了。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define S 55555
 4 #define inf 1987654321
 5 map<int,int>M;set<int>s;
 6 int n,k,sum[S],ls,c,t[S],T[S],p[S];
 7 void chg(int p,int w){for(;p<=c;p+=p&-p)t[p]=max(t[p],w);}
 8 int ask(int p,int w=-inf){for(;p;p^=p&-p)w=max(w,t[p]);return w;}
 9 void CHG(int p,int w){for(;p<=c;p+=p&-p)T[p]=min(T[p],w);}
10 int ASK(int p,int w=inf){for(;p;p^=p&-p)w=min(w,T[p]);return w;}
11 int gt(int x){return c-M[*s.lower_bound(x)];}
12 int main(){//freopen("1.in","r",stdin);
13     scanf("%d%d",&n,&k); s.insert(0);s.insert(-inf);s.insert(inf);
14     for(int i=1;i<=n;++i)scanf("%d",&sum[i]),sum[i]+=sum[i-1],s.insert(sum[i]);
15     for(auto x:s)M[x]=++c;c++;
16     for(int i=1;i<=n;++i)p[i]=c-M[sum[i]];
17     int l=-500000000,r=500000000,md,ans;
18     while(l<=r){
19         md=l+r>>1;
20         for(int i=1;i<=c;++i)t[i]=-inf,T[i]=inf;chg(c-M[0],0);CHG(c-M[0],0);
21         for(int i=1;i<n;++i)chg(p[i],1+ask(gt(sum[i]-md))),CHG(p[i],1+ASK(gt(sum[i]-md)));
22         if(ask(gt(sum[n]-md))+1>=k&&k>=1+ASK(gt(sum[n]-md)))r=ans=md,r--;else l=md+1;
23     }cout<<ans<<endl;
24 }
View Code

T3:生成树求和

大意:图,求所有生成树的权值和。权值定义为边权做3进制不进位加法的结果。$n le 40,w le 10000$

都提到进制了,就按位考虑呗。

每一位只能是0/1/2。我们考虑每条边有几种,然后就变成了《生成树》。

复杂度$O(n^6 log w)$。有30分。如果写二维拉格朗日插值能少个n卡常后在自家OJ可以AC。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define mod 1000000007
 4 int E[10][44][44],n,m,ans,o[44][44],oc,mt[44][44],pw[44][44],a[888][888];
 5 void add(int&a,int b){a+=b;if(a>=mod)a-=mod;}
 6 void dec(int&a,int b){add(a,mod-b);}
 7 int qp(int b,int t,int a=1){for(;t;t>>=1,b=1ll*b*b%mod)if(t&1)a=1ll*a*b%mod;return a;}
 8 int MT(int a=1){
 9     for(int i=1;i<n;++i){
10         int iv=qp(mt[i][i],mod-2);a=1ll*a*mt[i][i]%mod;for(int j=1;j<n;++j)mt[i][j]=1ll*mt[i][j]*iv%mod;
11         for(int j=i+1;j<n;++j)for(int k=n-1;k>=i;--k)dec(mt[j][k],1ll*mt[j][i]*mt[i][k]%mod);
12         a=1ll*a*mt[i][i]%mod;
13     }return (a+mod)%mod;
14 }
15 void Gauss(){
16     for(int i=1;i<=oc;++i){
17         if(!a[i][i]){for(int j=i+1;j<=oc;++j)if(a[j][i]){swap(a[i],a[j]);break;}}
18         int iv=qp(a[i][i],mod-2);for(int j=1;j<=oc+1;++j)a[i][j]=1ll*a[i][j]*iv%mod;
19         for(int j=1;j<=oc;++j)if(i!=j)for(int k=oc+1;k>=i;--k)dec(a[j][k],1ll*a[j][i]*a[i][k]%mod);
20     }
21 }
22 int main(){
23     memset(E,0xff,sizeof E);
24     scanf("%d%d",&n,&m);
25     for(int a,b,w,i=1;i<=m;++i){scanf("%d%d%d",&a,&b,&w);for(int j=0;j<10;++j)E[j][a][b]=w%3,w/=3;}
26     for(int i=0;i<n;++i)pw[i][0]=1;
27     for(int i=0;i<n;++i)for(int j=1;j<=n;++j)pw[i][j]=1ll*pw[i][j-1]*i%mod;
28     for(int x=0;x<n;++x)for(int y=0;x+y<n;++y)o[x][y]=++oc;
29     for(int b=0,r=1;b<10;++b,r*=3){
30         for(int i=1;i<=oc;++i)for(int j=1;j<=oc;++j)a[i][j]=0;
31         for(int x=0;x<n;++x)for(int y=0;x+y<n;++y){
32             for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)mt[i][j]=0;
33             for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)
34                 if(E[b][i][j]==1)add(mt[i][j],x),add(mt[j][i],x),dec(mt[i][i],x),dec(mt[j][j],x);
35                 else if(E[b][i][j]==2)add(mt[i][j],y),add(mt[j][i],y),dec(mt[i][i],y),dec(mt[j][j],y);
36                 else if(E[b][i][j]==0)add(mt[i][j],1),add(mt[j][i],1),dec(mt[i][i],1),dec(mt[j][j],1);
37             a[o[x][y]][oc+1]=MT();
38         }
39         for(int x=0;x<n;++x)for(int y=0;x+y<n;++y)for(int i=0;i<n;++i)for(int j=0;i+j<n;++j)a[o[x][y]][o[i][j]]=1ll*pw[x][i]*pw[y][j]%mod;
40         Gauss();
41         for(int x=0;x<n;++x)for(int y=0;x+y<n;++y)add(ans,(x+2ll*y)%3*a[o[x][y]][oc+1]%mod*r%mod);//cout<<ans<<endl;
42     }printf("%d
",n&1?ans:mod-ans);
43 }
30

正解挺神奇的。我们最后想知道的并不是每种1边权2边权各多少条的树有多少种,我们想知道的其实是权值和为它的树有多少。

也即(1边数+2边数×2)的树的数量。我们发现0边权是1,1边权是x,2边权是$x^2$。就解决了问题。

最后一棵权值和为$a$的树的贡献就是$x^a$。

权值和的上限是$2n-2$下限$0$。列一个$2n-1$的方程组,代$2n-1$个数进去解出来就行了。

这样复杂度瓶颈在矩阵树上了,一共$n log w$次。复杂度是$O(n^4 log w)$

剩下的高斯或者拉格朗日差别不大。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define mod 1000000007
 4 int E[10][44][44],n,m,ans,mt[44][44],pw[88][88],a[88][88];
 5 void add(int&a,int b){a+=b;if(a>=mod)a-=mod;}
 6 void dec(int&a,int b){add(a,mod-b);}
 7 int qp(int b,int t,int a=1){for(;t;t>>=1,b=1ll*b*b%mod)if(t&1)a=1ll*a*b%mod;return a;}
 8 int MT(int a=1){
 9     for(int i=1;i<n;++i){
10         int iv=qp(mt[i][i],mod-2);a=1ll*a*mt[i][i]%mod;for(int j=1;j<n;++j)mt[i][j]=1ll*mt[i][j]*iv%mod;
11         for(int j=i+1;j<n;++j)for(int k=n-1;k>=i;--k)dec(mt[j][k],1ll*mt[j][i]*mt[i][k]%mod);
12         a=1ll*a*mt[i][i]%mod;
13     }return (a+mod)%mod;
14 }
15 void Gauss(){
16     for(int i=1;i<n<<1;++i){
17         if(!a[i][i]){for(int j=i+1;j<n<<1;++j)if(a[j][i]){swap(a[i],a[j]);break;}}
18         int iv=qp(a[i][i],mod-2);for(int j=1;j<=n<<1;++j)a[i][j]=1ll*a[i][j]*iv%mod;
19         for(int j=1;j<n<<1;++j)if(i!=j)for(int k=n<<1;k>=i;--k)dec(a[j][k],1ll*a[j][i]*a[i][k]%mod);
20     }
21 }
22 int main(){
23     memset(E,0xff,sizeof E);scanf("%d%d",&n,&m);
24     for(int a,b,w,i=1;i<=m;++i){scanf("%d%d%d",&a,&b,&w);for(int j=0;j<10;++j)E[j][a][b]=w%3,w/=3;}
25     for(int i=1;i<n<<1;++i)pw[i][0]=1;
26     for(int i=1;i<n<<1;++i)for(int j=1;j<n<<1;++j)pw[i][j]=1ll*pw[i][j-1]*i%mod;
27     for(int b=0,r=1;b<10;++b,r*=3){
28         for(int x=1,y;x<n<<1;++x){y=x*x;
29             for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)mt[i][j]=0;
30             for(int i=1;i<=n;++i)for(int j=1;j<=n;++j)
31                 if(E[b][i][j]==1)dec(mt[i][j],x),dec(mt[j][i],x),add(mt[i][i],x),add(mt[j][j],x);
32                 else if(E[b][i][j]==2)dec(mt[i][j],y),dec(mt[j][i],y),add(mt[i][i],y),add(mt[j][j],y);
33                 else if(E[b][i][j]==0)dec(mt[i][j],1),dec(mt[j][i],1),add(mt[i][i],1),add(mt[j][j],1);
34             a[x][n<<1]=MT();
35         }
36         for(int x=1;x<n<<1;++x)for(int i=1;i<n<<1;++i)a[x][i]=pw[x][i-1];
37         Gauss();
38         for(int x=1;x<n<<1;++x)add(ans,(x-1ll)%3*a[x][n<<1]%mod*r%mod);
39     }printf("%d
",ans);
40 }
View Code

T4:圆圈游戏

原题《点点的圈圈

原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/12257630.html