模测8
07,25.
这次考试题忽然水辽。。
然而我也水辽///
这是预估期望/能力范围分数。
这是实际分数·「·」·「·」·「·」
曾经有一份巨水的T2,摆在我面前,
但是我没有好好珍惜?_?
如果给我一次机会,
我会把考后五分钟加上的不到30个字符码上去。
说总结:
T0:水题还是打的慢,不太熟,分析题意应该多想,整体思路应该多想,但是打代码应该速度。熟练度问题。以及思考速度。
T340分其实极限了,超出能力范围的东西还是先放一放。模拟考试就是模拟noip,总分更重要。
当时太想A掉T3了,而且想出了线段树优化的思路(当时没想清楚,以为线段树都是nlogn的,但实际上是(1/2n^2logn+1/2nlogn)的......还不如n^2DP)
制杖题目背景还提到平衡树和数据结构,然后我想出DP后就一直想平衡树或其他数据结构优化。
但实际上和数据结构一点关系都没有。。。
对于超出能力范围的题目,还是先保证拿到水题分数,再去刚。而且水题要多分析题意,但是做快点。
稳在考前,水在考中,刚在考后。
T1 kmp模板,但是hash好像好打,但是我忘了,但是别人好像都只会hash不会kmp,所以只有我觉得kmp比hash简单???
T2 题意理解:1、割点。2、能分开1,n。即删去当前点后能将1,n分为两个联通快。否则不是必经的,是无效的。
之所以觉得简单是没有思考全面。虽然考试时想到了要分开1和n,但是并没有想全。没有写出好的样例。
有的分支路径能到达1和n,但是并非必经。
这题虽然不用再建一棵圆方树,但是此操作确实不熟,要看看。
但是建树的同学数组要开8倍,因为数组没开够被卡到80,可见建树不够优秀。
垃圾比大神吴孟周建树:
#include<iostream> #include<cstdio> #include<cstring> #include<vector> #define db(x) cerr<<#x<<"="<<x<<endl using namespace std; const int N=200010; inline int read() { int x=0; char ch=getchar(); while(!isdigit(ch)) ch=getchar(); while(isdigit(ch)) { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x; } int n,m,dcc; int tot,head[N*3],nxt[N*8],to[N*8]; int low[N],dfn[N],num,stack[N],top; int fa[3*N],bl[N]; vector<int> ve[N*2]; inline void add(int a,int b) { to[++tot]=b; nxt[tot]=head[a]; head[a]=tot; } bool cut[N]; void tarjan(int x,int from) { low[x]=dfn[x]=++num; stack[++top]=x; int flag=0; for(int i=head[x];i;i=nxt[i]) { if(!dfn[to[i]]) { tarjan(to[i],i); low[x]=min(low[x],low[to[i]]); if(low[to[i]]>=dfn[x]) { flag++; if(flag>1||x!=1) cut[x]=1; ++dcc; int y; do{ y=stack[top--]; bl[y]=dcc; ve[dcc].push_back(y); }while(y!=to[i]); bl[x]=dcc; ve[dcc].push_back(x); } } else if(i!=(from^1)) low[x]=min(low[x],dfn[to[i]]); } } void dfs1(int x) { for(int i=head[x];i;i=nxt[i]) { if(to[i]==fa[x]) continue; fa[to[i]]=x; dfs1(to[i]); } } bool v[N*3]; int main() { //有自环有重边 //有自环有重边 //有自环有重边 int T=read(); while(T--) { for(int i=1;i<=dcc;++i) ve[i].clear(); tot=1; num=0; top=0; dcc=0; memset(dfn,0,sizeof dfn); memset(head,0,sizeof head); memset(cut,0,sizeof cut); memset(v,0,sizeof v); memset(fa,0,sizeof fa); memset(bl,0,sizeof bl); n=read(); m=read(); for(int i=1,a,b;i<=m;++i) { a=read(); b=read(); add(a,b); add(b,a); } tarjan(1,0); memset(head,0,sizeof head); tot=1; for(int i=1;i<=dcc;++i) for(unsigned j=0;j<ve[i].size();++j) if(cut[ve[i][j]]) add(i,ve[i][j]+dcc),add(ve[i][j]+dcc,i); dfs1(cut[1]?dcc+1:bl[1]); int x=cut[n]?n+dcc:bl[n]; while(fa[x]) v[x]=1,x=fa[x]; int cnt=0; for(int i=2;i<n;++i) if(v[i+dcc]) ++cnt; printf("%d\n",cnt); for(int i=2;i<n;++i) if(v[i+dcc]) printf("%d ",i); puts(""); } return 0; }
考前觉得tarjan不太熟,本来想看看,但是觉得这么新的知识点不会考,成功把自己hack
T3 要卡常。。。没必要的数组清零memset卡掉我5分。本来是40分。。。
或者LL数组改成int。。因为只能过小点,用不着LL,而且跑的慢要卡一卡,当然要用int。卡卡出奇迹。
n^2DP理论40,但也有50的不知道怎么卡的,要去看看。
山大附的郭神打的三分,本来我也看出了单谷的性质,是三分,但是没学过。。。
线段树优(tui)化思路:
线段树区间修改标记,区间加,区间最小值。
维护:节点存某点的值
sl,sr:前后缀1的个数
p:sl和sr的修改标记
f,g:DP数组中的单点值。f=f(i),g=g(i+1)。
断点c向右移动,遇到1时,f和g除断点处外是不会变的。而断点处u与上一个1的位置有关。预处理后可以O(1)求。线段树中logn改。
遇到0,整个加减(断点特殊):f-=sl,g+=sr;但是每个点都要改,相当于重新建树,单次nlogn.
极限数据总复杂度1/2n^2logn+1/2nlogn;
min(B,R)<=30,稳过。但是自己能想出新的思路,虽然退化了,但还是值得成就感。
正解:在枚举断点(n)和分界点(n)的n^2 DP基础上:
手模加感性思考可以发现断点向右移动时,分界点单调右移。(在拆环成2n的链基础上)(同为顺时针)
则枚举断点,分界点可以用while找。(分界点在"0"个数的中位(注意奇偶)证明见skyh和yxs这两尊机油大神)
在已知断点和分界点后,w=f+g可以O(1)求。用前缀和预处理,小容斥减一下,减去多余部分即可。
p为分界点。
LL w=f[p]-f[i]-(LL)(i-sl[i])*(sl[p]-sl[i])+
g[p+1]-g[i+n+1]-(LL)(hx-sr[i+n+1]-(i+n))*(sr[p+1]-sr[i+n+1]);
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #define F(i,a,b) for(rg int i=a;i<=b;++i) #define il inline #define LL long long #define cst const #define rg register #define pf(a) printf("%3d ",a) #define PF(a) printf("%3lld ",a) #define phn printf("\n") using namespace std; int read(); //-2,n,1 #define NX 1000010 #define min(a,b) (a<b?a:b) #define me(a) memset(a,0,sizeof(a)) char zfc[NX]; int a[NX<<1],sl[NX<<1],sr[NX<<1],sum0[NX<<1],tot0; LL f[NX<<1],g[NX<<1]; int n; const LL MAXN=0x7ffffffffff; int main(){ int T=read(); while(T--){ tot0=0; scanf("%s",zfc+1); n=strlen(zfc+1); F(i,1,n){ if(zfc[i]=='B')a[i]=1,a[i+n]=1; else a[i]=0,a[i+n]=0,++tot0; } rg int hx=(n<<1); F(i,1,(n<<1)){ f[i]=f[i-1]+1ll*a[i]*(i-1-sl[i-1]); sl[i]=sl[i-1]+a[i]; sum0[i]=sum0[i-1]+(a[i]==0?1:0); } for( int i=(n<<1);i>=1;--i){ g[i]=g[i+1]+1ll*a[i]*(hx-i-sr[i+1]); sr[i]=sr[i+1]+a[i]; } // F(i,1,n<<1)pf(i);phn; // F(i,1,n<<1)PF(f[i]);phn; // F(i,1,n<<1)PF(g[i]);phn; // F(i,1,n<<1)pf(sl[i]);phn; // F(i,1,n<<1)pf(sr[i]);phn; //LL w=f[9]-f[1]-(LL)(1-sl[1])*(sl[9]-sl[1]); //PF(w); LL ans=MAXN;rg int p=1; const int goal=(tot0&1)?(tot0/2+1):(tot0/2); for( int i=1;i<=n;++i){ while(sum0[p]-sum0[i]<goal){ ++p; //if(p>n*2)p=1; } LL w=f[p]-f[i]-(LL)(i-sl[i])*(sl[p]-sl[i])+ g[p+1]-g[i+n+1]-(LL)(hx-sr[i+n+1]-(i+n))*(sr[p+1]-sr[i+n+1]); // PF(w); ans=min(ans,w); } // phn; printf("%lld\n",ans); } return 0; } il int read(){ rg int s=0,f=0;rg char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=getchar();} while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+(ch^48);ch=getchar();} return f?-s:s; } /* g++ 1.cpp -g ./a.out 2 BBRBBRBBBRRR RRRBBBRBBRBB */ //多测,对拍??
另:郭神T3三分可做:
#include<cstdio> #include<iostream> #include<cmath> #include<algorithm> #include<cstring> #include<vector> #include<queue> #define ll long long using namespace std; const int MAXN=1000005; int T,n,pos; ll suml[MAXN*2],sumr[MAXN*2],ans=0x3f3f3f3f3f3f3f3f; int bcntl[MAXN*2],bcntr[MAXN*2],rcnt[MAXN*2]; char s[MAXN*2]; inline void R() { char c=getchar(); while(c!='B'&&c!='R') c=getchar(); while(c=='B'||c=='R') s[++n]=c,c=getchar(); } ll calcl(int l,int r) { return suml[r]-suml[l-1]-(ll)bcntl[l-1]*(rcnt[r]-rcnt[l-1]); } ll calcr(int l,int r) { return sumr[l]-sumr[r+1]-(ll)bcntr[r+1]*(rcnt[r]-rcnt[l-1]); } ll js(int l,int p,int r) { return calcl(l,p)+calcr(p+1,r); } ll find(int l,int r) { while(pos<=2*n) if(js(l,pos+1,r)<=js(l,pos,r)) pos++; else break; return js(l,pos,r); } int main() { scanf("%d",&T); while(T--) { R(); for(int i=1;i<=n;++i) s[n+i]=s[i]; suml[0]=0; bcntl[0]=rcnt[0]=0; for(int i=1;i<=n*2;++i) { suml[i]=suml[i-1]; bcntl[i]=bcntl[i-1]; rcnt[i]=rcnt[i-1]; if(s[i]=='B') ++bcntl[i]; else ++rcnt[i]; if(s[i]=='R') suml[i]+=bcntl[i]; } sumr[2*n+1]=0; bcntr[2*n+1]=0; for(int i=2*n;i>=1;i--) { sumr[i]=sumr[i+1]; bcntr[i]=bcntr[i+1]; if(s[i]=='B') ++bcntr[i]; else sumr[i]+=bcntr[i]; } pos=1; for(int i=1;i<=n;i++) ans=min(ans,find(i,i+n-1)); printf("%lld\n",ans); n=0;ans=0x3f3f3f3f3f3f3f3f; } return 0; }
观察规律找性质。
(这wmz和zdy真是稳如gou)
所以一定要把该拿的分都拿到,那就是top3