Noip模拟7

T1 匹配

题目描述
image.png


考场上忘记如何打KMP了,于是就用暴力hash水过去了。
然而数组开小了,痛失5pts...

CODE

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
namespace EMT
{
    int read()
    {
        int x = 0, f = 1;
        char ch = getchar();
        while (ch < '0' || ch > '9')
        {
            if (ch == '-')
                f = -1;
            ch = getchar();
        }
        while (ch >= '0' && ch <= '9')
            x = x * 10 + ch - '0', ch = getchar();
        return x * f;
    }
    #define F(i, a, b) for (register int i = a; i <= b; i++)
    #define f(x) for (register int i = head[x], j; i; i = e[i].next)
    #define pf printf
    inline void pi(int x)
    {
        pf("%d ", x);
    }
    inline void pn() { printf("\n"); }
    inline void ps(int a[], int size)
    {
        F(i, 1, size)
        pi(a[i]);
        pn();
    }const int N=1e6+100;
    #define ull unsigned long long
    int T,lena,lenb;char sa[N],t;
    ull hasha[N],hashb[N],p[N];
    inline short main(){
        T=read();p[0]=1;
        F(i,1,N-100)p[i]=p[i-1]*131;
        while(T--){
            lena=read();lenb=read();
            scanf("%s",sa+1);t=getchar();
            while(t>'z'||t<'a')t=getchar();
            F(i,1,lena)hasha[i]=hasha[i-1]*131+sa[i];
            F(i,1,lenb)hashb[i]=hasha[i];lenb++;
            hashb[lenb]=hashb[lenb-1]*131+t;
            int ans=0;
            for(register int i=min(lena,lenb);i>=1;i--)
            if(hasha[i]==hashb[lenb]-hashb[lenb-i]*p[i]){ans=i;break;}
            pi(ans);pn();
        }return 0;
    }
}
int main(){return EMT::main();}

T2 回家

题目描述
image.png


考场上忘记tj如何求割点了。。。还是得复习一下。

本题就是先求出每个点的DFN和LOW,

从1节点开始遍历到n并记录路径,

只有路上的割点才能算作必经点,记录输出即可。

Code

#include <iostream>
#include <cstdio>
#include <cmath>
#include <stack>
#include <cstring>
#include <algorithm>
using namespace std;
namespace EMT
{
    int read()
    {
        int x = 0, f = 1;
        char ch = getchar();
        while (ch < '0' || ch > '9')
        {
            if (ch == '-')
                f = -1;
            ch = getchar();
        }
        while (ch >= '0' && ch <= '9')
            x = x * 10 + ch - '0', ch = getchar();
        return x * f;
    }
    #define F(i, a, b) for (register int i = a; i <= b; i++)
    #define f(x) for (register int i = head[x], j; i; i = e[i].next)
    #define pf printf
    inline void pi(int x)
    {
        pf("%d ", x);
    }
    inline void pn() { printf("\n"); }
    inline void ps(int a[], int size)
    {
        F(i, 1, size)
        pi(a[i]);
        pn();
    }
    const int N=8e5+100;
    int rec[N],cou,T,n,m,co,tim,head[N],root,dfn[N],low[N],fa[N];bool bg[N],v[N],in[N];struct node{int next,to;}e[N<<1];
    void add(int next,int to){e[++co].next=head[next],e[co].to=to,head[next]=co;}
    inline void tj(int x){
        low[x]=dfn[x]=++tim;int fl=0;
        f(x){
            j=e[i].to;
            if(!dfn[j]){
                tj(j);fa[j]=x;
                low[x]=min(low[x],low[j]);
            }else low[x]=min(low[x],dfn[j]);
        }
    }
    int main(){
      //  freopen("home1.in","r",stdin);
       // freopen("my.out","w",stdout);
        T=read();
        while(T--){
            n=read(),m=read();
            memset(head,0,sizeof(head));memset(dfn,0,sizeof(dfn));
            memset(bg,0,sizeof(bg));memset(in,0,sizeof(in));
            memset(low,0,sizeof(low));memset(v,0,sizeof(v));
            memset(fa,0,sizeof(fa));
            co=cou=tim=0;
            F(i,1,m){
                int x=read(),y=read();
                if(x==y)continue;
                add(x,y);add(y,x);
            }
            tj(1);
            int x=n;
            while(x!=1){
              int  f=fa[x];
                if(f!=1&&f!=n&&low[x]>=dfn[f])rec[++cou]=f;
                x=f;
            }
            sort(rec+1,rec+cou+1);
            pi(cou);pn();
            F(i,1,cou)pi(rec[i]);
            pn();
        }
        return 0;
    }
}
int main(){return EMT::main();}

T3 寿司

题目描述
image.png


在与掰的误导与ICEY的正确引导下A掉了。

引用一下:

发现对于一个序列,肯定是左边一部分往左靠,右边一部分往右靠,于是可以每次二分出这个边界点。

时间复杂度\((nlogn)\),可以得到80到100分。

下面是我的思路:

\(li\),\(ri\)为i点左边和右边分别有几个B

发现答案是\(\sum_{i=1}^{n}min(li,ri)\)

先暴力统计出原序列的答案,再枚举其他序列。

对于一个新序列,不妨认为是上一个序列的第一个字母移到最后一位产生的,

那么如果移动的字母是\(R\)\(min(li,ri)\)不变仍为0,对其他\(R\)\(li\),\(ri\)没有影响,直接跳过

否则,序列中的\(R\)每个\(li-1\),\(ri+1\).

想一下,如果一个\(R\)原来的\(li<=ri\),那么\(li\)变成\(li-1\),更小了,ans相应减一,\(li>=ri+2\)的话同理会加一,\(li=ri+1\)的话答案不变,

用前缀和可以\(O(1)\)求出\(li\),\(ri\).二分出上述所说的两种产生贡献的\(R\)个数即可。

Code

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
using namespace std;
namespace EMT{
    #define F(i,a,b) for(register int i=a;i<=b;i++)
    #define D(i,a,b) for(register int i=a;i>=b;i--)
    #define pf printf
    typedef long long ll;
    void pi(ll x){pf("%lld ",x);}void pn(){pf("\n");}void ps(int a[],int size){F(i,1,size)pi(a[i]);pn();}
    inline int read(){int x=0;char ch=getchar();while(ch<'0'||ch>'9')ch=getchar();while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();return x;}
    const int N=1e6+100;ll end,tot;
    int T;ll cnt,totb,n,a[N<<1],lr[N<<1];char s[N];
    inline ll abs(ll a){return a<0?-a:a;}
    inline ll min(ll a,ll b){return a<b?a:b;}
    inline short main(){
        T=read();
        while(T--){
            scanf("%s",s+1);
            int Len=strlen(s+1);
            totb=cnt=end=n=tot=0;
            memset(a,0,sizeof(a));memset(lr,0,sizeof(lr));
            F(i,1,Len){
                n++;
                if(s[i]=='B')a[n]=1,lr[n]=cnt,totb++;
                else lr[n]=++cnt;
            }
            F(i,n+1,n*2)a[i]=a[i-n];
            F(i,1,2*n)a[i]=a[i-1]+a[i];
            F(i,n+1,n*2)lr[i]=lr[i-1]+(a[i]==a[i-1]);
            F(i,1,n)if(a[i]==a[i-1])tot+=min(a[i],(totb-a[i]));end=tot;
            F(i,1,n-1){
                if(a[i]==a[i-1])continue;
                int l=i,r=n+i-1,ans1=i-1;
                while(l<=r){
                    int mid=(l+r)>>1;
                    if(((a[mid]-a[i-1])<<1)<=totb)l=mid+1,ans1=mid;
                    else r=mid-1;
                }
                l=ans1+1,r=n+i-1;int ans2=i+n;
                while(l<=r){
                    int mid=(l+r)>>1;
                    if(((a[mid]-a[i-1])<<1)>=totb+2)ans2=mid,r=mid-1;
                    else l=mid+1;
                }
                tot-=(lr[ans1]-lr[i-1])-(lr[i+n-1]-lr[ans2-1]);
                end=min(tot,end);
            }
            pi(end);pn();
        }return 0;
    }
}
signed main(){return EMT::main();}
posted @ 2021-06-11 17:52  letitdown  阅读(75)  评论(0编辑  收藏  举报