9.8<1>题解

T1

 我也忘了我考场上在干什么了,好像是在想什么最长链拓扑序之类乱七八糟的东西,所以离正解有十万八千里的距离,现在我也看不懂我当时打了个啥,反正是连了边,用了优先队列,然后WA了

正解是用线段树优化DP,但是我连DP都没想到,就别提线段树了,先想一下最裸的DP,设$f[i][j]$代表处理完前$i$个水晶,已经选择的水晶中最小的$A$值为$j$的最大摧毁个数,考虑如何转移

如果$A_i{\leq}B_i$那么他只能摧毁炸毁的最小的$A$大于$B_i$的集合炸毁,所以说

$f[i][A_i]=max(f[i-1][B_i+1],f[i-1][B_i+2],{\cdots},f[i-1][maxx])+1$

如果$A_i>B_i$那么因为$f$数组第二维是最小值,所以最小值如果被更新为$A_i$了,那之前的最小值一定是${\leq}A_i$的,所以有

$f[i][A_i]=max(f[i-1][A_i],f[i-1][A_i+1],f[i-1][A_i+2],{\cdots},f[i-1][maxx])+1$

当然了,也有可能$A_i$对集合中的最小值没有造成影响,那么就是

$f[i][A_i]=f[i-1][j]+1$ $j{\in}(B_i,A_i)$

我们发现对$i$的贡献只由对$i-1$的贡献转移而来,取$max$对应了区间求最值,都+1对应了区间加法,对$f[i][A_i]$的修改对应了单点修改,所以把问题移到线段树上就可以了

 1 #include
 2 #include
 3 #include
 4 #include
 5 #define maxn 100100
 6 using namespace std;
 7 struct node{
 8     int zuo,you,w,lan;
 9 }tr[maxn*8];
10 int n;
11 int a[maxn],b[maxn],lsh[maxn*2];
12 void build(int fa,int l,int r)
13 {
14     tr[fa].zuo=l;  tr[fa].you=r;
15     if(l==r)  return ;
16     int mid=(l+r)>>1;
17     build(2*fa,l,mid);  build(2*fa+1,mid+1,r);
18 }
19 void down(int fa)
20 {
21     tr[2*fa].lan+=tr[fa].lan;  tr[2*fa+1].lan+=tr[fa].lan;
22     tr[2*fa].w+=tr[fa].lan;  tr[2*fa+1].w+=tr[fa].lan;
23     tr[fa].lan=0;
24 }
25 void up(int fa)
26 {
27     tr[fa].w=max(tr[2*fa].w,tr[2*fa+1].w);
28 }
29 void add(int fa,int l,int r)
30 {
31     if(tr[fa].zuo>=l&&tr[fa].you<=r)  {tr[fa].lan++;  tr[fa].w++;  return ;}
32     if(tr[fa].lan)  down(fa);
33     int mid=(tr[fa].zuo+tr[fa].you)>>1;
34     if(l<=mid)  add(2*fa,l,r);
35     if(r>mid)  add(2*fa+1,l,r);
36     up(fa);
37 }
38 void change(int fa,int pos,int w)
39 {
40     if(tr[fa].zuo==tr[fa].you)  {tr[fa].w=max(tr[fa].w,w);  return ;}
41     int mid=(tr[fa].zuo+tr[fa].you)>>1;
42     if(tr[fa].lan)  down(fa);
43     if(pos<=mid)  change(2*fa,pos,w);
44     else  change(2*fa+1,pos,w);
45     up(fa);
46 }
47 int query(int fa,int l,int r)
48 {
49     if(tr[fa].zuo>=l&&tr[fa].you<=r)  return tr[fa].w;
50     int mid=(tr[fa].zuo+tr[fa].you)>>1,ans=0;
51     if(tr[fa].lan)  down(fa);
52     if(l<=mid)  ans=max(ans,query(2*fa,l,r));
53     if(r>mid)  ans=max(ans,query(2*fa+1,l,r));
54     return ans;
55 }
56 int main()
57 {
58 //    freopen("ex_leader2.in","r",stdin);
59     scanf("%d",&n);
60     for(int i=1;i<=n;++i)  {scanf("%d%d",&a[i],&b[i]);  lsh[i]=a[i];  lsh[i+n]=b[i];}
61     sort(lsh+1,lsh+2*n+1);
62     int len=unique(lsh+1,lsh+2*n+1)-lsh-1;
63     for(int i=1;i<=n;++i)
64     {
65         a[i]=lower_bound(lsh+1,lsh+len+1,a[i])-lsh;
66         b[i]=lower_bound(lsh+1,lsh+len+1,b[i])-lsh;
67     }
68     build(1,1,len);
69     for(int i=1;i<=n;++i)
70     {
71         if(a[i]<=b[i])  {int chan=query(1,b[i]+1,len)+1;  change(1,a[i],chan);}
72         else  {add(1,b[i]+1,a[i]);  int chan=query(1,a[i]+1,len)+1;  change(1,a[i],chan);}
73     }
74     printf("%d\n",tr[1].w);
75     return 0;
76 }
View Code

T2

启发式合并or线段树合并,当时不想打数据结构,所以咕到现在

T3

考场上想到了一毛一样的dp定义,最后死在了对答案的容斥上,出现本质相同的子序列,一定是有一个字母出现了第二边导致本质相同,那就找到前面第一个和当前相同的字母,然后刨掉和那个字母相同的就可以了

设$dp[i][j]$代表以第$i$个位置为结尾的长度为$j$的本质不同的串有多少个,会重复的就是以前面的相同字母结束的串,所以$dp[i][j]=dp[i-1][j]+dp[i-1][j-1]-dp[las-1][j-1]$,注意一下数组越界的问题,可能会死

 1 #include
 2 #include
 3 #include<string>
 4 #include
 5 #define ll long long
 6 #define mod 998244353
 7 #define maxn 3030
 8 using namespace std;
 9 int len,cd,flag;
10 int pos[30];
11 char s[3010];
12 ll f[maxn][maxn];
13 int main()
14 {
15 //    freopen("coin.in","r",stdin);
16     scanf("%s%d",s+1,&len);  cd=strlen(s+1);
17     for(int i=2;i<=cd;++i)
18         if(s[i]!=s[i-1])  {flag=1;  break;}
19     if(!flag)  {printf("1\n");  return 0;}
20     else
21     {
22         for(int i=1;i<=cd;++i)
23         {
24             if(pos[s[i]-'a']==0)  f[i][1]=f[i-1][1]+1;
25             else  f[i][1]=f[i-1][1];
26             for(int j=2;j<=min(i,len);++j)
27             {
28                 if(pos[s[i]-'a']!=0)
29                     f[i][j]=((f[i-1][j]+f[i-1][j-1])%mod-f[pos[s[i]-'a']-1][j-1]+mod)%mod;
30                 else
31                     f[i][j]=(f[i-1][j]+f[i-1][j-1])%mod;
32             }
33             pos[s[i]-'a']=i;
34         }
35     }
36     printf("%lld\n",f[cd][len]);
37     return 0;
38 }
View Code

 

你可能感兴趣的:(9.8<1>题解)