noip模拟测试8


T1:给定一个字符串a和一个字符串b,b是a的前缀

  若在b串后添加一个字符x,求a串的前缀与b串的后缀的最长相同长度。

  (lb <= la <= 2*lb  lb <= 100,000)

  kmp裸题。。。

  然而考试时没看清数据范围,只开了两倍的 lb ,91分再见

  于是扔一波板子

  

1 for(int i=2;i<=n;i++) {
2     int p=i-1;
3     while(p&&s[i]!=s[nxt[p]+1]) p=nxt[p];
4     if(s[i]==s[nxt[p]+1]) nxt[i]=nxt[p]+1;
5     else nxt[i]=0;
6 }

 

T2:给定一张n个点m条边的无向图,求从1走到n的必经点。
  (n <= 200,000 m<=2*n)(可能会有自环和重边)
  一看必经点,显然是裸 tarjan,开开心心5分钟打完 tarjan 求割点的板子,输出所有割点,然后跑过样例就去看T3    :)
  T3想不出来,闲的无聊开始造T2的数据,结果造了随便一组就把自己干掉了。。。
  如图:
  
  尴尬的发现割点不一定是必经点!
  所以 Ctrl+A ,Delete :(
  (重构是好的 o_o#)
 
  重新构思,还是要 tarjan ,涉及到割点,又不是简单的计算和判定,那就是点双缩点或圆方树了
  然而学会圆方树后就从没写过复杂,细节多,占空间大,又难写的点双缩点了
  于是开始想怎么用圆方树计算答案
  
  因为圆方树会将原图变成树,容易发现答案就是圆方树上从1到n的链上的圆点
  所以做完了
 
  什么?你说你还不会圆方树?
  来康 小粉兔的博客
  
  什么?你还不会tarjan? 请跳过这道题……
  
  
 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cstring>
 6 #include<vector>
 7 #include<queue>
 8 #define ll long long
 9 using namespace std;
10 const int MAXN=200005;
11 int T,n,m,dfn[MAXN],low[MAXN],stk[MAXN],fa[MAXN*2],num,tp,dcc_cnt;
12 bool flag;
13 inline int R() {
14     int a=0;char c=getchar();
15     while(c>'9'||c<'0')c=getchar();
16     while(c>='0'&&c<='9')a=a*10+c-'0',c=getchar();
17     return a;
18 }
19 struct node {
20     int to,nxt;
21 }mp[MAXN*8];
22 int h[MAXN*2],tot;
23 void add(int x,int y) {
24     mp[++tot].to=y;mp[tot].nxt=h[x];h[x]=tot;
25 }
26 void tarjan(int u) {
27     dfn[u]=low[u]=++num;
28     stk[++tp]=u;
29     for(int i=h[u];i;i=mp[i].nxt) {
30         int v=mp[i].to;
31         if(!dfn[v]) {
32             tarjan(v);
33             low[u]=min(low[u],low[v]);
34             if(low[v]==dfn[u]) {
35                 ++dcc_cnt;
36                 int tmp;
37                 do {
38                     tmp=stk[tp--];
39                     add(dcc_cnt+n,tmp); add(tmp,dcc_cnt+n);
40                 }while(tmp!=v);
41                 add(dcc_cnt+n,u); add(u,dcc_cnt+n);
42             }
43         }
44         else low[u]=min(low[u],dfn[v]);
45     }
46 }
47 void dfs(int u) {
48     if(u==n) {
49         flag=1;
50         return;
51     }
52     for(int i=h[u];i;i=mp[i].nxt) {
53         int v=mp[i].to;
54         if(v==fa[u]||(u<=n&&v<=n)) continue;
55         fa[v]=u;
56         dfs(v);
57         if(flag) return;
58     }
59 }
60 int main() {
61     T=R();
62     while(T--) {
63         n=R();m=R();
64         for(int i=1,aa,bb;i<=m;i++) {
65             aa=R(),bb=R();
66             add(aa,bb); add(bb,aa);
67         }
68         tarjan(1);
69         dfs(1);
70         int p=fa[n];
71         tp=0;
72         while(p!=1) {
73             if(p<n) stk[++tp]=p;
74             p=fa[p];
75         }
76         sort(stk+1,stk+tp+1);
77         printf("%d\n",tp);
78         for(int i=1;i<=tp;i++) printf("%d ",stk[i]);
79         printf("\n");
80         memset(dfn,0,sizeof(dfn));
81         memset(low,0,sizeof(low));
82         memset(h,0,sizeof(h));
83         memset(fa,0,sizeof(fa));
84         num=0;tot=0;tp=0;flag=0;dcc_cnt=0;
85     }
86     return 0;
87 }
t2 Code

 


T3:T组数据,给一个环,环上有n个物品,物品有两种颜色,每次操作可以交换相邻的两个物品

  求使相同颜色的物品全部相邻的最少操作次数

  (T<= 10  n <= 1,000,000)
  显然正解 O ( n ) 然而想想后会发现只会写暴力模拟
  再仔细一想发现好像模拟都不会写……
  
  没办法,因为不会模拟,所以只能想想怎么计算
  首先可以将一种颜色看作物品,另一种看作空位,原操作相当与移动物品
  
  思考之后,会发现如果断环成链,那么一种方案就等价于:
  在序列上选一段长度为n的子序列,再在中间选择一个断点
  然后将断点左侧的物品都向左移,右侧的向右移,使其都靠在两边,代价最小。
 
  显然以上述思路,复杂度可以做到 O ( (找区间复杂度)×(找断点复杂度)×(计算代价复杂度) )
  如果枚举区间和断点的话,复杂度就为O ( n2 ×(计算代价复杂度) )
  
  那么就想如何将计算代价做到 O ( 1 )
  发现如果 O ( n ) 预处理前缀k个点移到最左和后缀k个点移到最右的代价,即前缀和后缀
  我们就可以在 O ( 1 ) 计算出某段代价(可以手玩几组数据,自己想一想)
  
  那么我们就可以做到 O ( n2 ) 的复杂度了
  再仔细一想对于每个区间来说,断点位置和代价组成的函数应该是一个单谷的函数(虽然并不严格单调)
  那么我们就可以用三分来优化查找断点的复杂度
  我们的复杂度就会变为O ( n log(n) ) 
  (实际上是不正确的,因为不严格单调实际上并不能用三分来计算,但复杂度更优,可以拿来水分)
  
  最后发现枚举区间的复杂度无法优化,只能继续优化查找断点的复杂度
  于是经过严谨的证明(猜的,想打表证明我也不拦你),发现断点对于所有的区间满足单调性
  于是用一个指针来维护断点位置,每次暴力移动就好了
  复杂度为 O ( n )
  
  
 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cstring>
 6 #include<vector>
 7 #include<queue>
 8 #define ll long long
 9 using namespace std;
10 const int MAXN=1000005;
11 int T,n,pos;
12 ll suml[MAXN*2],sumr[MAXN*2],ans=0x3f3f3f3f3f3f3f3f;
13 int bcntl[MAXN*2],bcntr[MAXN*2],rcnt[MAXN*2];
14 char s[MAXN*2];
15 inline void R() {
16     char c=getchar();
17     while(c!='B'&&c!='R') c=getchar();
18     while(c=='B'||c=='R') s[++n]=c,c=getchar();
19 }
20 ll calcl(int l,int r) {
21     return suml[r]-suml[l-1]-(ll)bcntl[l-1]*(rcnt[r]-rcnt[l-1]);
22 }
23 ll calcr(int l,int r) {
24     return sumr[l]-sumr[r+1]-(ll)bcntr[r+1]*(rcnt[r]-rcnt[l-1]);
25 }
26 ll js(int l,int p,int r) {
27     return calcl(l,p)+calcr(p+1,r);
28 }
29 ll find(int l,int r) {
30     while(pos<2*n)
31         if(js(l,pos+1,r)<=js(l,pos,r)) pos++;
32         else break;
33     return js(l,pos,r);
34 }
35 int main() {
36     scanf("%d",&T);
37     while(T--) {
38         R();
39         for(int i=1;i<=n;++i) s[n+i]=s[i];
40         suml[0]=0; bcntl[0]=rcnt[0]=0;
41         
42         for(int i=1;i<=n*2;++i) {
43             suml[i]=suml[i-1];
44             bcntl[i]=bcntl[i-1]; rcnt[i]=rcnt[i-1];
45             if(s[i]=='B') ++bcntl[i];
46             else ++rcnt[i];
47             if(s[i]=='R') suml[i]+=bcntl[i];
48         }
49         sumr[2*n+1]=0; bcntr[2*n+1]=0;
50         for(int i=2*n;i>=1;i--) {
51             sumr[i]=sumr[i+1];
52             bcntr[i]=bcntr[i+1];
53             if(s[i]=='B') ++bcntr[i];
54             else sumr[i]+=bcntr[i];
55         }
56         pos=1;
57         for(int i=1;i<=n;i++)
58             ans=min(ans,find(i,i+n-1));
59         printf("%lld\n",ans);
60         n=0;ans=0x3f3f3f3f3f3f3f3f;
61     }
62     return 0;
63 }
t3 Code

 


 

posted @ 2019-07-26 19:30  G_keng  阅读(204)  评论(2编辑  收藏  举报