2019-7-25 考试总结

A. 匹配

考试代码($WA18$)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#define max(x,y) ((x)>(y)?(x):(y))
#define Maxn 100050
#define INF 0x7fffff
#define k1 10007
#define k2 109
#define mod1 1000000007
#define mod2 1000000009
#define Reg register
using namespace std;
int la,lb,t;
char B[Maxn];
long long lss1,lss2,has11[Maxn],has12[Maxn],has21[Maxn],has22[Maxn],pow1[Maxn],pow2[Maxn];
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        has11[0]=has12[0]=has21[0]=has22[0]=0;
        scanf("%d%d",&la,&lb);
        string S,K; cin>>S;
        for(Reg int i=1,x;i<=min(lb+1,la);++i)
        {
            x=S[i-1];
            has11[i]=(has11[i-1]*k1+x-'a'+1)%mod1;
            has12[i]=(has12[i-1]*k2+x-'a'+1)%mod2;
            B[i]=x;
        }
        cin>>K;
        for(Reg int i=0;i<3;++i)
        {
            if(K[i]>='a'&&K[i]<='z')
            {
                B[++lb]=K[i];
                break;
            }
        }
        pow1[0]=pow2[0]=1;
        for(Reg int i=1;i<=lb;++i)
        {
            has21[i]=(has21[i-1]*k1+B[i]-'a'+1)%mod1;
            has22[i]=(has22[i-1]*k2+B[i]-'a'+1)%mod2;
            pow1[i]=pow1[i-1]*k1%mod1;
            pow2[i]=pow2[i-1]*k2%mod2;
        }
        int ans=0;
        for(Reg int i=lb;i>=1;--i)
        {
            lss1=(has21[lb]-has21[i]*pow1[lb-i]%mod1+mod1)%mod1;
            lss2=(has22[lb]-has22[i]*pow2[lb-i]%mod2+mod2)%mod2;
            if(lss1==has11[lb-i]&&lss2==has12[lb-i]) ans=max(ans,lb-i);
        }
        printf("%d\n",ans);
    }
    return 0;
}
View Code

$AC$代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#define max(x,y) ((x)>(y)?(x):(y))
#define Maxn 100050
#define INF 0x7fffff
#define k1 10007
#define k2 109
#define mod1 1000000007
#define mod2 1000000009
#define Reg register
using namespace std;
int la,lb,t;
char B[Maxn];
long long lss1,lss2,has11[Maxn],has12[Maxn],has21[Maxn],has22[Maxn],pow1[Maxn],pow2[Maxn];
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        has11[0]=has12[0]=has21[0]=has22[0]=0;
        scanf("%d%d",&la,&lb);
        string S,K; cin>>S;
        for(Reg int i=1,x;i<=min(lb+1,la);++i)
        {
            x=S[i-1];
            has11[i]=(has11[i-1]*k1+x-'a'+1)%mod1;
            has12[i]=(has12[i-1]*k2+x-'a'+1)%mod2;
            B[i]=x;
        }
        cin>>K;
        for(Reg int i=0;i<3;++i)
        {
            if(K[i]>='a'&&K[i]<='z')
            {
                B[++lb]=K[i];
                break;
            }
        }
        pow1[0]=pow2[0]=1;
        for(Reg int i=1;i<=lb;++i)
        {
            has21[i]=(has21[i-1]*k1+B[i]-'a'+1)%mod1;
            has22[i]=(has22[i-1]*k2+B[i]-'a'+1)%mod2;
            pow1[i]=pow1[i-1]*k1%mod1;
            pow2[i]=pow2[i-1]*k2%mod2;
        }
        int ans=0;
        for(Reg int i=lb;i>=0;--i)
        {
            lss1=(has21[lb]-has21[i]*pow1[lb-i]%mod1+mod1)%mod1;
            lss2=(has22[lb]-has22[i]*pow2[lb-i]%mod2+mod2)%mod2;
            if(lss1==has11[lb-i]&&lss2==has12[lb-i]) ans=max(ans,lb-i);
        }
        printf("%d\n",ans);
    }
    return 0;
}
View Code

低错总结:

枚举不够。。从$l_b$枚举到了$1$,结果$WA18$,

然后考试后把$1$改成了$0$,然后过了。

 

B. 回家

考试代码($WA 20$)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<map>
#define min(x,y) ((x)<(y)?(x):(y))
#define Maxn 200050
#define Reg register
using namespace std;
int biao[Maxn];
int t,n,m,tot,top,ans,ok,fir[Maxn],fic[Maxn],vis[Maxn];
int root,indx,cnt,dfn[Maxn],low[Maxn],cur[Maxn],stack[Maxn],pos[Maxn],num[Maxn];
struct Tu {int st,ed,next;} lian[Maxn*2],liap[Maxn*2];
vector<vector<int> > dcc(Maxn/10);
void add(int x,int y)
{
    lian[++tot].st=x;
    lian[tot].ed=y;
    lian[tot].next=fir[x];
    fir[x]=tot;
    return;
}
void adp(int x,int y)
{
    liap[++top].st=x;
    liap[top].ed=y;
    liap[top].next=fic[x];
    fic[x]=top;
    return;
}
void tarjan(int x,int fat)
{
    dfn[x]=low[x]=++indx;
    if(x==root&&!fir[x]) {dcc[++cnt].push_back(x); return;}
    stack[++stack[0]]=x;
    int flag=0;
    for(Reg int i=fir[x];i;i=lian[i].next)
    {
        if(!dfn[lian[i].ed])
        {
            tarjan(lian[i].ed,x);
            low[x]=min(low[x],low[lian[i].ed]);
            if(low[lian[i].ed]>=dfn[x])
            {
                ++flag;
                if(flag>0||x!=root) cur[x]=1;
                ++cnt;
                while(stack[stack[0]]!=lian[i].ed)
                    dcc[cnt].push_back(stack[stack[0]--]);
                dcc[cnt].push_back(stack[stack[0]--]);
                dcc[cnt].push_back(x);
            }
        }
        else if(lian[i].ed!=fat)
            low[x]=min(low[x],dfn[lian[i].ed]);
    }
    return;
}
void dfs(int x)
{
    vis[x]=1;
    if(x==pos[n]) {ok=1; return;}
    if(ok) return;
    for(Reg int i=fic[x];i;i=liap[i].next)
    {
        if(!vis[liap[i].ed])
        {
            if(biao[liap[i].ed]) stack[++stack[0]]=biao[liap[i].ed];
            dfs(liap[i].ed);
            if(!ok) --stack[0];
            else return;
        }
    }
    return;
}
void init()
{
    tot=top=ans=ok=root=indx=cnt=stack[0]=0;
    for(Reg int x=1;x<=n;++x)
    {
        biao[x]=fir[x]=fic[x]=cur[x]=vis[x]=dfn[x]=low[x]=pos[x]=num[x]=0;
        if(x<Maxn) dcc[x].clear();
    }
    return;
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        init();
        for(Reg int i=1,x,y;i<=m;++i)
        {
            scanf("%d%d",&x,&y);
            add(x,y); add(y,x);
        }
        for(Reg int i=1;i<=n;++i)
            if(!dfn[i]) root=i,tarjan(i,i);
        int num=cnt;
        for(Reg int i=1;i<=n;++i) if(cur[i]) pos[i]=++num,biao[num]=i;
        for(Reg int i=1,l;i<=cnt;++i)
        {
            l=dcc[i].size();
            for(Reg int j=0,x;j<l;++j)
            {
                x=dcc[i][j];
                if(cur[x]) {adp(pos[x],i); adp(i,pos[x]);}
                else pos[x]=i;
            }
        }
        cnt=num; stack[0]=0;
        dfs(pos[1]);
        memset(vis,0,sizeof(vis)); ans=0;
        for(Reg int i=1;i<=stack[0];++i)
        {
            if(!vis[stack[i]]&&stack[i]!=n&&stack[i]!=1)
            {
                ++ans;
                vis[stack[i]]=1;
            }
        }
        printf("%d\n",ans);
        for(Reg int i=1;i<=n;++i) if(vis[i]) printf("%d ",i);
        printf("\n");
    }
    return 0;
}
View Code

$AC$代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#define min(x,y) ((x)<(y)?(x):(y))
#define Maxn 200050
#define Reg register
using namespace std;
int biao[Maxn*5];
int t,n,m,tot,top,ans,ok,fir[Maxn*5],fic[Maxn*5],vis[Maxn*5];
int root,indx,cnt,dfn[Maxn*5],low[Maxn*5],cur[Maxn],stack[Maxn*5],pos[Maxn*5];
struct Tu {int st,ed,next;} lian[Maxn*8],liap[Maxn*8];
vector<vector<int> > dcc(Maxn);
void add(int x,int y)
{
    lian[++tot].st=x;
    lian[tot].ed=y;
    lian[tot].next=fir[x];
    fir[x]=tot;
    return;
}
void adp(int x,int y)
{
    liap[++top].st=x;
    liap[top].ed=y;
    liap[top].next=fic[x];
    fic[x]=top;
    return;
}
void tarjan(int x)
{
    dfn[x]=low[x]=++indx;
    stack[++stack[0]]=x;
    if(x==root&&!fir[x]) {dcc[++cnt].push_back(x); return;}
    int flag=0;
    for(Reg int i=fir[x];i;i=lian[i].next)
    {
        if(!dfn[lian[i].ed])
        {
            tarjan(lian[i].ed);
            low[x]=min(low[x],low[lian[i].ed]);
            if(low[lian[i].ed]>=dfn[x])
            {
                ++flag; if(flag>0||x!=root) cur[x]=1;
                ++cnt;
                while(stack[stack[0]]!=lian[i].ed)
                    dcc[cnt].push_back(stack[stack[0]--]);
                dcc[cnt].push_back(stack[stack[0]--]);
                dcc[cnt].push_back(x);
            }
        }
        else low[x]=min(low[x],dfn[lian[i].ed]);
    }
    return;
}
void dfs(int x)
{
    vis[x]=1;
    if(x==pos[n]) {ok=1; return;}
    if(ok) return;
    for(Reg int i=fic[x];i;i=liap[i].next)
    {
        if(!vis[liap[i].ed])
        {
            if(biao[liap[i].ed]) stack[++stack[0]]=biao[liap[i].ed];
            dfs(liap[i].ed);
            if(biao[liap[i].ed]&&!ok) --stack[0];
            if(ok) return;
        }
    }
    return;
}
void init()
{
    tot=top=ans=ok=root=indx=cnt=stack[0]=0;
    for(Reg int x=0;x<=n*2;++x)
        biao[x]=fir[x]=fic[x]=cur[x]=vis[x]=dfn[x]=low[x]=pos[x]=0;
    return;
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        init();
        for(Reg int i=1,x,y;i<=m;++i)
        {
            scanf("%d%d",&x,&y);
            add(x,y); add(y,x);
        }
        for(Reg int i=1;i<=n;++i)
            if(!dfn[i]) root=i,tarjan(i);
        int num=cnt;
        for(Reg int i=1;i<=n;++i) if(cur[i]) {pos[i]=++num; biao[num]=i;}
        for(Reg int i=1,l;i<=cnt;++i)
        {
            l=dcc[i].size();
            for(Reg int j=0,x;j<l;++j)
            {
                x=dcc[i][j];
                if(cur[x]) {adp(pos[x],i); adp(i,pos[x]);}
                else pos[x]=i;
            }
            dcc[i].clear();
        }
//        cout<<endl;
//        for(Reg int i=1;i<=n;++i) cout<<pos[i]<<' ';
//        cout<<endl;
        cnt=num; stack[0]=0; ok=0;
        dfs(pos[1]);
        memset(vis,0,sizeof(vis)); ans=0;
        for(Reg int i=1;i<=stack[0];++i)
        {
            if(!vis[stack[i]]&&stack[i]!=n&&stack[i]!=1)
            {
                ++ans;
                vis[stack[i]]=1;
            }
        }
        printf("%d\n",ans);
        for(Reg int i=1;i<=n;++i) if(vis[i]) printf("%d ",i);
        printf("\n");
    }
    return 0;
}
View Code

低错总结:

1、数组没开够。

2、板子不熟练,考试调$Tarjan$调了半小时,然后最后还没打对。

$Tarjan$找割点的板子

void tarjan(int x)
{
    dfn[x]=low[x]=++indx;
    stack[++stack[0]]=x;
    if(x==root&&!fir[x]) {dcc[++cnt].push_back(x); return;}
    int flag=0;
    for(Reg int i=fir[x];i;i=lian[i].next)
    {
        if(!dfn[lian[i].ed])
        {
            tarjan(lian[i].ed);
            low[x]=min(low[x],low[lian[i].ed]);
            if(low[lian[i].ed]>=dfn[x])
            {
                ++flag; if(flag>1||x!=root) cur[x]=1;
                ++cnt;
                while(stack[stack[0]]!=lian[i].ed)
                    dcc[cnt].push_back(stack[stack[0]--]);
                dcc[cnt].push_back(stack[stack[0]--]);
                dcc[cnt].push_back(x);
            }
        }
        else low[x]=min(low[x],dfn[lian[i].ed]);
    }
    return;
}

考试时码的:

void tarjan(int x,int fat)
{
    dfn[x]=low[x]=++indx;
    if(x==root&&!fir[x]) {dcc[++cnt].push_back(x); return;}
    stack[++stack[0]]=x;
    int flag=0;
    for(Reg int i=fir[x];i;i=lian[i].next)
    {
        if(!dfn[lian[i].ed])
        {
            tarjan(lian[i].ed,x);
            low[x]=min(low[x],low[lian[i].ed]);
            if(low[lian[i].ed]>=dfn[x])
            {
                ++flag;
                if(flag>0||x!=root) cur[x]=1;
                ++cnt;
                while(stack[stack[0]]!=lian[i].ed)
                    dcc[cnt].push_back(stack[stack[0]--]);
                dcc[cnt].push_back(stack[stack[0]--]);
                dcc[cnt].push_back(x);
            }
        }
        else if(lian[i].ed!=fat)
            low[x]=min(low[x],dfn[lian[i].ed]);
    }
    return;
}

3、

void dfs(int x)
{
    vis[x]=1;
    if(x==pos[n]) {ok=1; return;}
    if(ok) return;
    for(Reg int i=fic[x];i;i=liap[i].next)
    {
        if(!vis[liap[i].ed])
        {
            if(biao[liap[i].ed]) stack[++stack[0]]=biao[liap[i].ed];
            dfs(liap[i].ed);
            if(!ok) --stack[0];
            else return;
        }
    }
    return;
}

这个地方的弹栈弹过了,出现负数。。

改了一点:

if(biao[liap[i].ed]&&!ok) --stack[0];
if(ok) return;

然后过了。。

 

C. 寿司

$O(n^2)$的算法:

预处理出$sum[i]$表示$i$节点之前$1$的个数。

枚举区间和断点,

对于区间$[L,R]$,假设断点是$Mid$,

那么$Mid$左侧的$1$都应该移到最左端,右侧的$1$都应该移到最右端,

那么左侧需要移动的步数就是$W_1=\sum \limits_{i=L,num[i]=1}^{Mid} i-(L+sum[i]-sum[L]-1)$

右侧需要移动的步数就是$W_2=\sum \limits_{i=Mid+1,num[i]=1}^{R} R-sum[R]+sum[i-1]+1-i$

现在复杂度是$O(n^3)$,

然后化简一下就可以发现可以用前缀和。

最后就是$O(n^2)$的复杂度。

$O(n)$的算法:

决策单调性。。

当区间右移的时候,最优断点不可能左移。

至于证明:

有一个数学的方法,有点麻烦。

在$n^2$的算法中我们能够化出一个式子:

另$clac[i]$表示在当前状态中$i$点的最优步数,

那么$clac[i]= K\times sum[i] +sum1[i] -sum2[i]+P$,

其中

$sum1[i]=\sum \limits_{j=1}^{i} (j-sum[j])$

$sum2[i]=\sum \limits_{j=1}^{i} (sum[j-1]-j)$

$P=sum2[R]-sum1[L-1]+(R-sum[R]+1)\times sum[R]-(sum[L-1]-L+1)\times sum[L-1]$

$K=sum[L-1]+sum[R]-(L+R)$。

令在区间$[L,R]$中断点为$mid$时最优,

那么肯定在$[L,R]$中$clac[mid]<=clac[mid-1]$,

也就是:

$K\times sum[mid-1]+sum1[mid-1]-sum2[mid-1]+P>=K\times sum[mid] +sum[mid]-sum2[mid]+P$

再移个项:

$(K-1)\times num[mid]-2\times sum[mid]+2\times mid<=0$ ---------(*),

我们要证明在区间$[L+1,R+1]$中仍然满足这个性质:

接下来这个$P$可以消掉,不管它,看$K$。

当$L++,R++$时,

$K'=sum[L]+sum[R+1]-(L+R)-2$,

$K'=sum[L-1]+sum[R]-(L+R)-2+num[L]+num[R+1]$,

显然这个$K'<=K$,那么上边的(*)式子仍然满足。

所以就得出来了。

丑陋的代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#define min(x,y) ((x)<(y)?(x):(y))
#define int long long
#define INF 0x7fffffffffffff
#define Maxn 4000050
#define Reg register
using namespace std;
int t,n,P,K,mid,ans,L,R;
int sum[Maxn],sum1[Maxn],sum2[Maxn],num[Maxn];
char A[Maxn];
int clac(int x) {return K*sum[x]+sum1[x]-sum2[x]+P;}
signed main()
{
    scanf("%lld",&t);
    while(t--)
    {
        ans=INF;
        scanf("%s",A);
        n=strlen(A);
        sum[0]=sum1[0]=sum2[0]=0;
        for(Reg int i=1;i<=n;++i)
        {
            sum[i]=sum[i-1];
            sum1[i]=sum1[i-1];
            sum2[i]=sum2[i-1];
            if(A[i-1]=='R')
            {
                ++sum[i]; num[i]=1;
                sum1[i]+=i-sum[i];
                sum2[i]+=sum[i-1]-i;
            }
            else num[i]=0;
        }
        for(Reg int i=n+1;i<=2*n;++i)
        {
            num[i]=num[i-n];
            sum[i]=sum[i-1];
            sum1[i]=sum1[i-1];
            sum2[i]=sum2[i-1];
            if(num[i]==1)
            {
                ++sum[i];
                sum1[i]+=i-sum[i];
                sum2[i]+=sum[i-1]-i;
            }
        }
        L=1,R=n;
        P=sum2[R]-sum1[L-1]+(R-sum[R]+1)*sum[R]-(sum[L-1]-L+1)*sum[L-1];
        K=sum[L-1]-L+1-R+sum[R]-1;
        mid=0;
        for(Reg int i=1;i<=n;++i) //枚举左端点
        {
            L=i,R=i+n-1;
            P=sum2[R]-sum1[L-1]+(R-sum[R]+1)*sum[R]-(sum[L-1]-L+1)*sum[L-1];
            K=sum[L-1]-L+1-R+sum[R]-1;
            ans=min(ans,clac(mid));
            while(mid+1<=R&&clac(mid+1)<=clac(mid)) //决策单调性处理
            {
                ans=min(ans,clac(mid+1));
                ++mid;
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}
View Code

 

当然也有更简单的证法:

$wd$大神题解,传送门

 

总结:

$T1$第一眼看是$KMP$,然后不会打。。。

打了一个$Hash$,然后$WA18$。

觉得自己之前打的是个假的$Hash$,

$T2$基本上是个$Tarjan$的板子,然后$WA20$,

这次$T1$和$T2$基本上都是送分题,然后就爆炸了。

板子不会,代码能力差,低错。。。。

然后最后$18+20+0=38$,

没什么水平。。。

最后忠告:

$AC$一道题需要$100+$行的代码,然而从$AC$到$WA$仅仅需要$1$个字符。

所以一定要打对拍。。

 

posted @ 2019-07-25 17:10  Milk_Feng  阅读(186)  评论(1编辑  收藏  举报