2021 icpc 沈阳游记

热身赛:

某题读假题+写假代码,搞了最难题1h未果之后跑路了

rk200+,感觉状态血差,正赛要寄(反向flag++

 

正赛:

刚把基本的板子打完发现edgnb已经被过穿了(edgnb!

签到后yfz说能写M,于是写写写,写完过不了样例,似乎做法假了

换我写签到题B,很快啊就WA了,又改一个很明显的错误之后收获-2,半小时了还是1题,感觉要完

然后就是两个队友分别写F、J两道签到题,我在旁边看我的代码怎么看怎么对

此时已经发现了数组开小,但一直很疑惑为什么不给返回RE

直到zzx已经开始不那么签到的I题+自己又看了20min无果,决定改一下MAXN重交一次,一下就过了

被自己气晕.jpg

 

在旁边发现了看上去和假期多校一道点双贪心很像的边双贪心H,讨论了一下感觉十分正确

正好zzx不负众望的过了I(除了中途因为复数除法写错一起看了至少20min

我直接进行一个H的写,写完进行了一些加强样例的测,交上去1a了(get第二个正赛1a 好耶

看了眼排名rk22,L也会写了,感觉前途一片光明(flag++

zzx写完了一部分后让我写另一部分树dp,我说可以,写完发现和样例好像差的有点远

重新理了一遍思路之后发现不但一个很基本的式子推错了,而且开始的容斥还假了

最后改完发现只有容斥+树dp+简单式子了,测了测之后收获一个WA

yfz再次用sa做M,旁边发现我的树dp写麻烦了,不是很确定这个dp的正确性

纠结的时候突然发现树的大小是$2n$,所以数组又双叒叕开小了,改了一下之后又过了

人麻了.jpg

 

想看榜的时候失败的时候才意识到已经封榜了,从来没感觉过时间过的这么快(?

最后半小时经历了sa板子出错,改板子+各种修改未果,最后还是没能通过M

 

赛后:

刚结束就被告知卡线金了,感觉十分魔幻

亲眼看了一下榜发现2min险胜银首,属于是幸运拉满了

同时发现M和桂林的J好像几乎完全一致,但桂林的J我们vp时我用了后缀树(队友不会后缀树,我不会sa,完美错过.jpg

没能8题还是挺可惜的,感觉这8题都不是那么难

不过还是Au了 好!

 

补题:

$M$

首先有结论每次查询串的右端点一定是当前的点

对反串建后缀树,按经典方法对儿子字典序排序

倒着扫反串,容易知道每个点有可能成为最大值仅当该点$x$的$pos_x-mxlen_{fa[x]}$在范围内

对$pos$取一个$max$即可

 1 #include<bits/stdc++.h>
 2 #define inf 2139062143
 3 #define ll long long
 4 #define db double
 5 #define ld long double
 6 #define ull unsigned long long
 7 #define MAXN 2001001
 8 #define MOD 998244353
 9 #define Fill(a,x) memset(a,x,sizeof(a))
10 #define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
11 #define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
12 #define ren for(int i=fst[x];i;i=nxt[i])
13 #define pii pair<int,int>
14 #define fi first
15 #define se second
16 #define pb push_back
17 using namespace std;
18 inline ll read()
19 {
20     ll x=0,f=1;char ch=getchar();
21     while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
22     while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
23     return x*f;
24 }
25 namespace CALC
26 {
27     inline int pls(int a,int b){return a+b>=MOD?a+b-MOD:(a+b<0?a+b+MOD:a+b);}
28     inline int mns(int a,int b){return a-b<0?a-b+MOD:(a-b>=MOD?a-b-MOD:a-b);}
29     inline int mul(int a,int b){return (1LL*a*b)%MOD;}
30     inline void inc(int &a,int b){a=pls(a,b);}
31     inline void dec(int &a,int b){a=mns(a,b);}
32     inline void tms(int &a,int b){a=mul(a,b);}
33     inline int qp(int x,int t,int res=1)
34         {for(;t;t>>=1,x=mul(x,x)) if(t&1) res=mul(res,x);return res;}
35     inline int Inv(int x){return qp(x,MOD-2);}
36 }
37 using namespace CALC;
38 char s[MAXN];
39 vector<pii> G[MAXN];
40 int m,n,sum[MAXN<<2],ans[MAXN],len[MAXN],stp,dfn[MAXN];
41 int rt,las,tot,mxl[MAXN],fa[MAXN],pos[MAXN],g[MAXN];
42 int tr[MAXN][26];
43 ll c[MAXN],num[MAXN];
44 void extend(int c,int id)
45 {
46     int p=las,np=las=++tot;mxl[np]=mxl[p]+1;pos[np]=id;
47     for(;p&&!tr[p][c];p=fa[p]) tr[p][c]=np;
48     if(!p) {fa[np]=rt;return ;}int q=tr[p][c];
49     if(mxl[q]==mxl[p]+1) {fa[np]=q;return ;}
50     int nq=++tot;mxl[nq]=mxl[p]+1;pos[nq]=pos[q];
51     memcpy(tr[nq],tr[q],sizeof(tr[nq]));
52     fa[nq]=fa[q],fa[np]=fa[q]=nq;
53     for(;p&&tr[p][c]==q;p=fa[p]) tr[p][c]=nq;
54 }
55 void dfs(int x)
56 {
57     dfn[x]=++stp;sort(G[x].begin(),G[x].end());
58     for(auto e:G[x]) dfs(e.se),pos[x]=max(pos[x],pos[e.se]);
59 }
60 inline int getw(int x){return pos[x]-mxl[fa[x]];}
61 bool cmp(int x,int y){return getw(x)<getw(y);}
62 int main()
63 {
64     rt=las=tot=1;scanf("%s",s+1);n=strlen(s+1);
65     reverse(s+1,s+n+1);
66     rep(i,1,n) extend(s[i]-'a',i);int mx=0,res=0,j=tot;
67     rep(i,2,tot) G[fa[i]].pb({s[pos[i]-mxl[fa[i]]]-'a',i});
68     dfs(1);rep(i,1,tot) g[i]=i;sort(g+1,g+tot+1,cmp);
69     dwn(i,n,1)
70     {
71         for(;getw(g[j])>=i&&j;j--)
72             if(dfn[g[j]]>mx) res=g[j],mx=dfn[g[j]];
73         printf("%d %d\n",!res?1:n-pos[res]+1,n-i+1);
74     }
75 }
View Code

 

$G$

考虑一些性质:

1.最终的答案串中显然含有所有字符

2.每一个字符在答案串中只会出现连续的一段,否则取掉前面的一段字典序更大

3.越靠前的字符越多越好

因此只需要考虑如何求出一个每一个字符的连续段都尽可能长且包含所有字符的子序列

令$F[state]$表示已经取了$State$种类字符的答案串,且结束尽可能的早(因为这样会使后面答案更大

每次转移时枚举新的字符,要使该字符尽可能长的同时剩余集合内的字符之后仍然均可以被取到

需要预处理$pos[State]$表示$s[pos:len]$内包含$State$内的所有字符且$pos$尽可能的大

该数组倒着预处理一遍,再$nlogn$枚举子集即可

回到转移,已知$pos$与当前串的结束位置,利用前缀和可得到该字符的长度

此时新串的结束应当为$pos$前的最后一个字符,否则后续字符可能出现缺失

求每个位置前的$c$字符位置同样容易预处理

 1 #include<bits/stdc++.h>
 2 #define inf 2139062143
 3 #define ll long long
 4 #define db double
 5 #define ld long double
 6 #define ull unsigned long long
 7 #define MAXN 2001001
 8 #define MOD 998244353
 9 #define Fill(a,x) memset(a,x,sizeof(a))
10 #define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
11 #define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
12 #define ren for(int i=fst[x];i;i=nxt[i])
13 #define pii pair<int,int>
14 #define fi first
15 #define se second
16 #define pb push_back
17 using namespace std;
18 inline int read()
19 {
20     int x=0,f=1;char ch=getchar();
21     while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
22     while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
23     return x*f;
24 }
25 namespace CALC
26 {
27     inline int pls(int a,int b){return a+b>=MOD?a+b-MOD:(a+b<0?a+b+MOD:a+b);}
28     inline int mns(int a,int b){return a-b<0?a-b+MOD:(a-b>=MOD?a-b-MOD:a-b);}
29     inline int mul(int a,int b){return (1LL*a*b)%MOD;}
30     inline void inc(int &a,int b){a=pls(a,b);}
31     inline void dec(int &a,int b){a=mns(a,b);}
32     inline void tms(int &a,int b){a=mul(a,b);}
33     inline int qp(int x,int t,int res=1)
34         {for(;t;t>>=1,x=mul(x,x)) if(t&1) res=mul(res,x);return res;}
35     inline int Inv(int x){return qp(x,MOD-2);}
36 }
37 using namespace CALC;
38 int n,m,mxs,pre[1010][21],sum[1010][21],pos[MAXN],vis[25],las[21];
39 char s[1010];
40 struct data{int g[21],end;}dp[MAXN];
41 bool operator > (const data &x,const data &y)
42 {
43     rep(i,0,20) if(x.g[i]!=y.g[i]) return x.g[i]>y.g[i];
44     return x.end<y.end;
45 }
46 int main()
47 {
48     n=read();scanf("%s",s+1);rep(i,1,n) vis[s[i]-'a']=1;
49     rep(i,0,20) if(vis[i]) vis[i]=m++;mxs=(1<<m)-1;
50     rep(i,1,n)
51     {
52         rep(j,0,m-1) pre[i][j]=las[j],sum[i][j]=sum[i-1][j];
53         las[vis[s[i]-'a']]=i,sum[i][vis[s[i]-'a']]++;
54     }
55     rep(j,0,m-1) pre[n+1][j]=las[j];
56     int p,cnt,now=0;data tmp;
57     dwn(i,n,1) {now|=1<<vis[s[i]-'a'];if(!pos[now]) pos[now]=i;}
58     pos[0]=n+1;
59     dwn(i,mxs,1) rep(j,0,m-1) if((i>>j)&1)
60         pos[i^(1<<j)]=max(pos[i^(1<<j)],pos[i]);
61     rep(st,0,mxs) rep(i,0,m-1) if(!((st>>i)&1))
62     {
63         now=mxs^st,cnt=__builtin_popcount(st);
64         if(pos[now]<=dp[st].end) continue;
65         p=pre[pos[now^(1<<i)]][i],tmp=dp[st],tmp.end=p;
66         tmp.g[cnt]=sum[p][i]-sum[dp[st].end][i];
67         if(tmp>dp[st|(1<<i)]) dp[st|(1<<i)]=tmp;
68     }
69     rep(i,0,m-1) rep(j,1,dp[mxs].g[i]) putchar('a'+m-i-1);
70 }
View Code

 

posted @ 2021-11-24 18:46  jack_yyc  阅读(106)  评论(0编辑  收藏  举报