[考试反思]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 }
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 }
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",n&1?ans:mod-ans); 43 }
正解挺神奇的。我们最后想知道的并不是每种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\n",ans); 40 }
T4:圆圈游戏
原题《点点的圈圈》