NOIP improvement group simulation 2

Posted by exnet on Sat, 15 Jan 2022 22:29:58 +0100

A. Match

kmp board, match the suffix of B with the prefix of A, that is, use AB to find the next array, and the answer is \ (next[min(la,lb+1)] \)

Note that kmp matching is the largest prefix and suffix other than itself. It needs to be judged that B is just the prefix of A. the data in this question is strange, and the special judgment is 90pts

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=1000005;
char a[maxn],b[maxn];
int net[maxn];
void kmp(int la,int lb){
    memset(net,0,sizeof(net));
    int j=0;
    for(int i=2;i<=lb;++i){
        while(j&&b[j+1]!=b[i])j=net[j];
        if(b[j+1]==b[i])j++;
        net[i]=j;
    }
    printf("%d\n",net[lb]);
}
int main(){
    int T;scanf("%d",&T);
    for(int ask=1;ask<=T;++ask){
        int la,lb;scanf("%d%d",&la,&lb);
        scanf("%s",a+1);
        for(int i=1;i<=lb;++i)b[i]=a[i];
        ++lb;scanf("%c",&b[lb]);
        while(b[lb]<'a'||b[lb]>'z')scanf("%c",&b[lb]);
        if(lb<=la&&b[lb]==a[lb])printf("%d\n",lb);
        else kmp(la,lb);
    }
    return 0;
}

B. Go home

Algorithm 1: delete a point on the DFS record path every time, and then DFS will see if it can get an n score of 30pts (in the case of special judgment tree during the exam, the result is wrong, damn it)

Optimization: you don't need to delete points every time. dfs looks for a path. If there is a path from i-j (i, j is the point on the 1-n path) to a non-1-n path, then the points between i-j are not the points that must pass through. i thought of it in the examination room, but the time (code force) is not enough. The explanation of the question is 80-100pts

Positive solution:
I also thought of it during the exam, but because konjaku tarjan didn't finish (didn't learn), he had no choice but to explode..

It can be thought that the point that 1-n must pass through must be a cut point of the graph, but the cut point is not necessarily a point on the path

When tarjan cuts a point, just record whether it is on the path, that is, whether there is n in the subtree of the point

: this story tells us not to leave a hole

code

Retention pit

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int read(){
    char c;int x=0;
    c=getchar();
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9'){x*=10;x+=c-'0';c=getchar();}
    return x;
}
const int maxn=200005;
int n,m;
struct edge{
    int to,net;
}e[maxn<<2|1];
int head[maxn],tot;
void add(int u,int v){
    e[++tot].net=head[u];
    head[u]=tot;
    e[tot].to=v;
}
int low[maxn],dfn[maxn],tim;
bool flag[maxn];
bool vis[maxn];
bool lv[maxn];
bool tarjan(int x,int fx){
    dfn[x]=low[x]=++tim;lv[x]=1;
    for(int i=head[x];i;i=e[i].net){
        int v=e[i].to;if(v==fx)continue;
        if(!dfn[v]){
            vis[x]|=tarjan(v,x);
            low[x]=min(low[x],low[v]);
            if(low[v]>=dfn[x]&&vis[v])flag[x]=1;
        }
        else if(lv[v])low[x]=min(low[x],dfn[v]);
    }
    lv[x]=0;
    if(x==n||vis[x])return vis[x]=1;
    return 0;
}

int main(){
///  freopen("home.in","r",stdin);
//    freopen("home.out","w",stdout);
    int T;T=read();
    for(int ask=1;ask<=T;++ask){
        n=read();m=read();
        memset(head,0,sizeof(head));
        memset(vis,0,sizeof(vis));
        memset(flag,0,sizeof(flag));
        memset(dfn,0,sizeof(dfn));
        memset(low,0,sizeof(low));
        tim=0,tot=0;
        for(int i=1;i<=m;++i){
            int u,v;u=read();v=read();
            add(u,v);add(v,u);
        }
        tarjan(1,0);
        int cnt=0;
        for(int i=2;i<n;++i)if(vis[i]&&flag[i])cnt++;
        printf("%d\n",cnt);
        for(int i=2;i<n;++i)if(vis[i]&&flag[i])printf("%d ",i);
        printf("\n");
    }
    return 0;
}

C. Sushi

The success of the question misled me into thinking that it was the data structure (or the food) and even wanted to type the line segment tree (only this)

Obviously, it's a DP, a ring DP. It can be thought that at least one plate of sushi doesn't need to be moved, otherwise it's equivalent to turning the ring, which is meaningless. It can be broken into chains.

Consider one of R or B. continuity on a ring is equivalent to being at both ends or in the middle of the chain. Both sides are better considered.

It's easy to find that those on the left must go to the left, those on the right must go to the right, and there must be a dividing point p in the middle. Those on the left of p go to the left, and those on the right of p go to the right, and the interval moves to the right, that is, it's equivalent to turning clockwise (or counterclockwise) on the ring, and p must move or stay in the same direction, that is, p is monotonous. In this way, just scan it directly \ (O(n) \)

More specific

Preprocess the steps l[i],r[i] from I to the left and right, that is, there are several sushi numbers with different colors from I to the left or right. When enumerating the interval, l[i] minus the left bound l[],r[i] minus the right bound r [], that is, the steps I takes to both ends

When moving the interval, consider whether moving R or B on the left to the right has any impact on walking left and right. When moving p, record several walking left and several walking right

The idea is relatively clear, the code implementation is still very detailed (or I'm too good), and it's easy to calculate negative numbers

And remember to open longlong

#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=2000005;
long long min(long long x,long long y){return x<y?x:y;}
char c[maxn];
long long l[maxn],r[maxn];
int main(){
   int T;scanf("%d",&T);
   for(int ask=1;ask<=T;++ask){
       memset(l,0,sizeof(l));memset(r,0,sizeof(r));
       scanf("%s",c+1);int s=strlen(c+1),p;
       for(int i=1;i<=s;++i)c[i+s]=c[i];
       p=0;for(int i=1;i<=s<<1;++i){if(c[i]=='B')++p;l[i]=p;} 
       p=0;for(int i=s<<1;i>=1;--i){if(c[i]=='B')++p;r[i]=p;}
       p=0;long long ans=1e18,sum=0,ls=0,rs=0;
       for(int i=1;i<=s;++i)if(c[i]=='R'){sum+=r[i]-r[s+1];rs++;}
       for(int i=1;i<=s;++i){
           while(p+1<=i+s-1){
               if(c[p+1]=='B'){
                   p++;
               }else{
                   if(l[p+1]-l[i-1]<r[p+1]-r[i+s]){
                       p++;ls++;rs--;
                       sum=sum-r[p]+r[i+s]+l[p]-l[i-1];
                   }else break;
               }
           }         ans=min(ans,sum);
           if(c[i]=='B')sum=sum-ls+rs;
           else {--ls;++rs;}
       }
       printf("%lld\n",ans);
   }
   return 0;
}