noip模拟7[匹配·回家·寿司]

这次考试状态好像还是没有回来,只拿了55pts,还全是第一题的功劳,就是一个小KMP,然后还让我给打错了

就很难受,while打成了if,然后wa掉45分考完立马拿回来了,悔死了,害

第二题爆零了,为什么??问就是板子没背过,tarjan的割点,还有缩点(其实用不到,但是我也不会QWQ)

第三题也爆零了,为什么??问就是我是大傻瓜,看到题u就想睡觉,困的眼都睁不开

俺也不知道为啥,这睡得越多越困,为啥啊,不知道

这次考试其实给了我很大的信心,让我不要见到题就跑,毕竟还是有简单题的哈哈哈

下次会考的更好

 T1匹配

 这就是一个小KMP,hash也行,好像比我KMP还快,真不知道为啥

害,就直接KMP就完事了,好像我还打假了,别人都比我快

人家都是直接ab串去匹配,我是aa自己匹配,然后哦拿着b在上面跑

代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define re register int
 4 const int N=1000005;
 5 int T,la,lb;
 6 char a[N],b[N],p[5];
 7 int fail[N];
 8 signed main(){
 9     scanf("%d",&T);
10     while(T--){
11         memset(fail,0,sizeof(fail));
12         scanf("%d%d",&la,&lb);
13         scanf("%s",a+1);
14         //for(re i=1;i<=la;i++)cout<<a[i]<<" ";
15         for(re i=1;i<=lb;i++)b[i]=a[i];
16         scanf("%s",p);
17         b[++lb]=p[0];
18         int j=0;
19         for(re i=2;i<=la;i++){
20             //cout<<j<<" ";
21             if(a[j+1]!=a[i]&&j>0)j=fail[j];
22             //cout<<j<<" ";
23             if(a[j+1]==a[i])j++;
24             //cout<<j<<endl;
25             fail[i]=j;
26         }
27         j=0;int ans=0;
28         for(re i=0;i<=lb;i++){
29             while(b[i+1]!=a[j+1]&&j>0)j=fail[j];
30             if(b[i+1]==a[j+1]&&j<la)j++;
31             if(i+1==lb)ans=max(ans,j);
32             //cout<<j<<" "<<a[j]<<endl;
33         }
34         printf("%d\n",ans);
35     }
36 }
T1

T2回家

要不是我考场上把板子给忘了,至于拿零分???????

 这一看就能看出来是tarjan的割点的板子吧,可惜我忘了

然后 注意我们直接判断出来的割点并不是最终的必须经过的点

所以我们在判断割点的时候,加上一个判断,判断一下这个点能不能把n给割掉

就是定义一个pd数组

然后pd[n]=1;根据深搜的顺序向上转移

就行了,代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define re register int
 4 const int N=400005;
 5 int T,n,m;
 6 int to[N*2],nxt[N*2],head[N],rp;
 7 int dfn[N],low[N],cnt,cut[N];
 8 bool pd[N];
 9 int ans;
10 void add_edg(int x,int y){
11     to[++rp]=y;
12     nxt[rp]=head[x];
13     head[x]=rp;
14 }
15 void dfs(int x){
16     dfn[x]=low[x]=++cnt;
17     for(re i=head[x];i;i=nxt[i]){
18         int y=to[i];
19         if(!dfn[y]){
20             dfs(y);
21             low[x]=min(low[x],low[y]);
22             if(low[y]>=dfn[x])
23                 if(x!=1&&pd[y])
24                     cut[x]=1,ans++;
25             if(pd[y])pd[x]=1;
26         }
27         else low[x]=min(low[x],dfn[y]);
28     }
29 }
30 signed main(){
31     scanf("%d",&T);
32     while(T--){
33         rp=0;
34         //memset(low,0,sizeof(low));
35         cnt=0;
36         scanf("%d%d",&n,&m);
37         for(re i=1;i<=n;i++){
38             cut[i]=0;pd[i]=0;
39             dfn[i]=0;head[i]=0;
40         }
41         for(re i=1;i<=m;i++){
42             int x,y;
43             scanf("%d%d",&x,&y);
44             add_edg(x,y);
45             add_edg(y,x);
46         }
47         pd[n]=1;
48         ans=0;
49         dfs(1);
50         printf("%d\n",ans);
51         for(re i=2;i<n;i++)
52             if(cut[i])printf("%d ",i);
53         printf("\n");
54     }
55 }
T2

T3寿司

 不说这个题是毒瘤题吧,反正就挺迷的,因为,要你统计最小步数,然后我第一时间就想到了区间dp

然后想到了石子合并,然后思路就断了,然后我就想睡觉,然后就完蛋了

首先我们看到这个题,我们很容易看出来,我们要找到一个断点,分割点是把这个环分割成一个链

就是把环状的问题转换成链状的问题

然后我们就可以枚举这个断点了,肯定每一种情况的移动都不会经过断点(显然的)

我们设只动R,因为在R动的同时,交换,B也会跟着一起动

我们还可以找到一个分界点,就是这个分界点两侧的R都不会向相反的方向移动

就是左边的去左边,右边的去右边

还可以看出来,每一个R 移动的步数就是min(l[i],r[i]);(l[i]为R左侧B的数量,r[i]为R右侧B的数量)

这样我们就成功的搞出了O(n2)的算法:直接枚举断点,然后O(n)计算每个R的min(l[i],r[i]),然后求和即可,最后再取个最小值,40pts

然后我就去搞了个优化,优化完还是40就很无奈,然后去找正解了(我用的O(nlogn)的,还有O(n)的,不过那个人的常数有亿点点大,比我慢了3000ms)

现在考虑原来的式子:(设tot0表示R的个数,tot1表示B的个数,l[i]+r[i]=tot1)

            $  ans=\min\sum\limits_{i}^{tot_0}\min(l[i],r[i]) $

       $  ans=\min\sum\limits_{i}^{tot_0}\frac{l[i]+r[i]-\left | l[i]-r[i] \right | }{2} $

       $  ans=\min\sum\limits_{i}^{tot_0}\frac{tot_1-\left | l[i]-r[i] \right | }{2} $

       $  ans=sam+\max\sum\limits_{i}^{tot_0}\frac{\left | l[i]-r[i] \right | }{2} $(sam为tot1*tot0/2)

这时候,sam变成了定植,我们只需要去求后面的式子的最大值就行了

我们还要考虑由上一次向这一次转移,我们可以枚举这长度为n的区间的起点,直接在这个序列上枚举就可以了

从1~n,我们依次把第一个字符放到整个串的末尾,这样就模拟了,枚举每一个断点的过程

设x[i]=l[i]-r[i];

然后分类讨论,如果放的是R ,那么这个R的x[i]就变成tot1

       如果是B,那么每一个下x[i]都减去2

但是这里有一个非常恶心的地方,我们都知道正数减2,绝对值减小

负数减2,绝对值增加

但是如果是1的话,我们就要进行特判,不能操作,因为绝对值没变

所以我们将所有的x[i]加入到一个小根堆里,这样每次在堆首取得最小的正数,然后进行转移

你是不是在想,怎么对0进行操作,0我们把它放到负数中,因为它减2之后绝对值会增加

而且,关于如何对队列里的数进行操作,因为我们每一次转移都要对所有的x[i]-2,但是为了保证复杂度,我们有只能枚举堆头的几个值

所以我们用到一个变量seg,记录我们转移了几个B,这样我们每次将堆头与2*seg去比较,来判断是否弹出

然后记得转移R的时候,向队尾加入的是tot1+2*seg

然后这个题就愉快的解出来了,对于上面的解释可能有些模糊,所以可以先试着将队列全部弹出,-2后在push进去,然后再考虑我这个算法

代码

 

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define int long long
 4 #define re register int
 5 const int N=1000005;
 6 int T,n;
 7 char a[N];
 8 int tot[2];//0 R   1 B
 9 int l[N],r[N];
10 priority_queue<int,vector<int>,greater<int> > q;
11 signed main(){
12     scanf("%lld",&T);
13     while(T--){
14         while(!q.empty())q.pop();
15         memset(tot,0,sizeof(tot));
16         memset(l,0,sizeof(l));
17         int sum=0,maxn=0,zh=0,fu=0;
18         scanf("%s",a+1);
19         n=strlen(a+1);
20         for(re i=1;i<=n;i++){
21             l[i]=l[i-1];
22             if(a[i]=='R')tot[0]++;
23             else tot[1]++,l[i]++;
24         }
25         for(re i=1;i<=n;i++){
26             if(a[i]=='R'){
27                 r[i]=tot[1]-l[i];
28                 sum+=abs(l[i]-r[i]);
29                 if(l[i]-r[i]>0)q.push(l[i]-r[i]),zh++;
30                 else fu++;
31             }
32         }
33         int seg=0;
34         maxn=max(maxn,sum);
35         for(re i=1;i<n;i++){
36             if(a[i]=='B'){
37                 seg++;
38                 sum+=2*fu;
39                 while(!q.empty()){
40                     if(q.top()-seg*2>0)break;
41                     else if(q.top()-seg*2==0){
42                         sum-=2;zh--;fu++;
43                         q.pop();
44                     }
45                     else{
46                         zh--;fu++;
47                         q.pop();
48                     }
49                 }
50                 sum-=2*zh;
51                 maxn=max(maxn,sum);
52             }
53             else{
54                 zh++;fu--;
55                 q.push(tot[1]+2*seg);
56             }
57             //cout<<i<<endl;
58         }
59         printf("%lld\n",(tot[1]*tot[0]-maxn)/2);
60     }
61 }
T3

 

posted @ 2021-06-11 17:24  fengwu2005  阅读(149)  评论(0编辑  收藏  举报