noip模拟测试7[匹配·回家·寿司]
Posted hzoi------fengwu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了noip模拟测试7[匹配·回家·寿司]相关的知识,希望对你有一定的参考价值。
这次考试状态好像还是没有回来,只拿了55pts,还全是第一题的功劳,就是一个小KMP,然后还让我给打错了
就很难受,while打成了if,然后wa掉45分考完立马拿回来了,悔死了,害
第二题爆零了,为什么??问就是板子没背过,tarjan的割点,还有缩点(其实用不到,但是我也不会QWQ)
第三题也爆零了,为什么??问就是我是大傻瓜,看到题u就想睡觉,困的眼都睁不开
俺也不知道为啥,这睡得越多越困,为啥啊,不知道
这次考试其实给了我很大的信心,让我不要见到题就跑,毕竟还是有简单题的哈哈哈
下次会考的更好
这就是一个小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 }
要不是我考场上把板子给忘了,至于拿零分???????
这一看就能看出来是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 }
不说这个题是毒瘤题吧,反正就挺迷的,因为,要你统计最小步数,然后我第一时间就想到了区间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*tot2/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 }
以上是关于noip模拟测试7[匹配·回家·寿司]的主要内容,如果未能解决你的问题,请参考以下文章
8.9考试(NOIP模拟34)[Merchant·Equation·Rectangle]