模测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;
}
View Code

 

考前觉得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
*/
//多测,对拍??
View Code

另:郭神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;
}
View Code

 观察规律找性质。

(这wmz和zdy真是稳如gou)

所以一定要把该拿的分都拿到,那就是top3

posted @ 2019-07-26 09:02  seamtn  阅读(197)  评论(0编辑  收藏  举报