2019.8.1考试反思

八月开了一个完美的好头倒数加倍快乐加倍

日常看一遍题,觉得都不可做但是暴力分很好拿,于是快乐开始撕烤&暴力。

T1一看40分白送,然后想了想,和之前一道题排序非常想,排序是01串判断位置,思想还是差不多的。

然后我脑子长了锈了死活转不过弯来

看了看T2,手%样例后发现两个区间之间的地方其实是不能放的。

那么每个小区间都可以影响大区间,处理一下每个-1,码完收工。

T3貌似暴力可以骗40分?前缀和之后处理异或即可,用大暴力和暴力对拍。

T1并没有继续从之前堵住的地方绕出来。

然后就结束了。

题解部分

T1:

明显和排序异曲同工,排序是二分查找位置,将其转化成01串,而这道题明显可以看作是26次区间赋值

然后因为常数太鸡儿大被卡掉,然后循环展开可过我估计和T60的我没有什么关系

换一种思路,如果这一整个区间都是一样的,那么我们把它赋成对应的值,这样就避免了遍历整棵树

然后区间查询个数之后区间赋值 我并不会证明复杂度QwQ

某树袋熊双手上下摇摆:这样你在考场上就快乐的拿到了100分(雾)的好成绩

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #define reg register
 6 #define ls(k) k<<1
 7 #define rs(k) k<<1|1
 8 using namespace std;
 9 int const maxn=1e5+10;
10 struct node{int l,r,lz,w;}t[maxn*4];
11 int n,m,cnt[27];
12 char ch[maxn];
13 void debug(){for(int i=1;i<=26;i++) cout<<i<<" "<<cnt[i]<<endl;}
14 inline void build(int l,int r,int k)
15 {
16     t[k].l=l,t[k].r=r;
17     if(l==r)
18     {
19         t[k].w=ch[l]-'a'+1;
20         return ;
21     }
22     int mid=l+r>>1;
23     build(l,mid,ls(k));
24     build(mid+1,r,rs(k));
25     if(t[ls(k)].w==t[rs(k)].w) t[k].w=t[ls(k)].w;
26 }
27 inline void query(int l,int r,int k)
28 {
29     if(l<=t[k].l&&r>=t[k].r&&t[k].w)
30     {
31         cnt[t[k].w]+=t[k].r-t[k].l+1;
32         return ;
33     } 
34     if(t[k].w) t[ls(k)].w=t[k].w,t[rs(k)].w=t[k].w;
35     int mid=t[k].l+t[k].r>>1;
36     if(mid>=l) query(l,r,ls(k));
37     if(mid<r) query(l,r,rs(k));
38 }
39 inline void add(int l,int r,int k,int pos)
40 {
41     if((l<=t[k].l&&r>=t[k].r)||t[k].w==pos)
42     {
43         t[k].w=pos;
44         return ;
45     }
46     if(t[k].w) t[ls(k)].w=t[k].w,t[rs(k)].w=t[k].w;
47     int mid=t[k].l+t[k].r>>1;
48     if(mid>=l) add(l,r,ls(k),pos);
49     if(mid<r) add(l,r,rs(k),pos);
50     if(t[ls(k)].w==t[rs(k)].w) t[k].w=t[ls(k)].w;
51     else t[k].w=0;
52 }
53 inline void ask(int k)
54 {
55     if(t[k].w)
56     {
57         for(int i=t[k].l;i<=t[k].r;i++) printf("%c",t[k].w+'a'-1);
58         return ;
59     } 
60     ask(ls(k));ask(rs(k));
61 }
62 int main()
63 {    
64     scanf("%d%d",&n,&m);
65     scanf("%s",ch+1);
66     build(1,n,1);
67     for(reg int i=1;i<=m;i++)
68     {
69         for(int i=1;i<=26;i++) cnt[i]=0;
70         int opt,l,r;
71         scanf("%d%d%d",&l,&r,&opt);
72         query(l,r,1);
73         //debug();
74         if(opt)
75         {    
76             int tmp=l;
77             for(reg int j=1;j<=26;j++)
78             {
79                 if(!cnt[j]) continue;
80                 add(tmp,tmp+cnt[j]-1,1,j);
81                 tmp+=cnt[j];
82             }
83         }
84         else 
85         {
86             int tmp=l;
87             for(reg int j=26;j>=1;j--)
88             {
89                 if(!cnt[j]) continue;
90                 add(tmp,tmp+cnt[j]-1,1,j);
91                 tmp+=cnt[j];
92             }
93         }
94     }
95     ask(1);
96     return 0;
97 }
View Code

T2:

又是一个神仙dp,%%%Smily考场A掉TQLOTZ

我本来以为是组合数,码出来的**被我考试后脑子hack,天知道我怎么能这么想。

首先读题可以发现这道题和行几乎没有什么关系,然后我们用列转移。

设$ f[i][j] $为到达第 $i$ 列时左侧有$j$个右区间放了,$L$ 为左区间个数前缀和,$R$ 为右区间个数前缀和

当前状态可以从右区间没有放以及放了转移。

即 $f[i][j]=f[i-1][j]+f[i-1][j-1]*(R[i]-j+1)$

再考虑左区间。

我们可以得到,每一个包含在大区间的小区间都使对大区间中可选择的位置减少1个,所以用它来计算左区间贡献。

并不需要担心负数,因为一旦出现负数那么代表状态不合法。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #define ll long long
 5 using namespace std;
 6 int const maxn=3010;
 7 int const mod=998244353;
 8 ll n,m,l[maxn],r[maxn],L[maxn],R[maxn],f[maxn][maxn];
 9 int main()
10 {
11     scanf("%lld%lld",&n,&m);
12     for(int i=1;i<=n;i++) scanf("%lld%lld",&l[i],&r[i]),L[l[i]]++,R[r[i]]++;
13     for(int i=1;i<=m;i++) L[i]+=L[i-1],R[i]+=R[i-1];
14     f[0][0]=1;
15     for(int i=1;i<=m;i++)
16     {
17         f[i][0]=f[i-1][0];
18         for(int j=1;j<=i;j++) (f[i][j]+=f[i-1][j]+f[i-1][j-1]*(R[i]-j+1)%mod)%=mod; //右区间
19         for(int k=0;k<=i;k++)
20             for(int j=L[i-1];j<L[i];j++)
21                 (f[i][k]*=(i-k-j))%=mod; //左区间
22     }
23     printf("%lld",f[m][n]);
24 }
View Code

T3:

可以很明显看出来枚举断点来判断从而骗到40分然后我因为赋值的原因少了24TAT

首先它的柿子是一个类似于逻辑左移的东西,然后对手有m+1种不同的异或方案。

建一棵$01trie$进行$dfs$,因为对手总会尽量使你的最终结果更低,所以有两种情况:

1,如果当前节点有两个儿子,那么无论这一位是什么,对手都能把它异或成0,不考虑贡献向下走

2,只有1个儿子,那么我们选择另一个就可以使这一位有贡献,继续沿着$trie$走

最终到叶子就是答案,可以看到一开始的柿子对答案并没有影响,因为无论如何都能得到所有的数

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<map>
 5 using namespace std;
 6 int const maxn=1e5+10;
 7 int n,m,a[maxn],sum[maxn],yh[maxn],trie[30*maxn][2],tot,imax;
 8 map<int,int>mp;
 9 void insert(int x)
10 {
11     //cout<<x<<endl;
12     int rt=0;
13     for(int i=1<<(n-1);i>0;i>>=1)
14     {
15         int w=(x&i)?1:0;
16         if(!trie[rt][w]) trie[rt][w]=++tot;
17         rt=trie[rt][w];
18         //cout<<x<<" "<<w<<" "<<rt<<endl;
19     }
20     //cout<<endl;
21 }
22 void dfs(int x,int i,int now)
23 {
24     //cout<<x<<" "<<i<<" "<<now<<endl;
25     if(i==0)
26     {
27         imax=max(imax,now);
28         mp[now]++;
29         return ;
30     }
31     if(!trie[x][0]&&trie[x][1])
32     {
33         dfs(trie[x][1],i-1,(now|(1<<i-1)));
34     } 
35     if(trie[x][0]&&!trie[x][1])
36     {
37         dfs(trie[x][0],i-1,(now|(1<<i-1)));
38     } 
39     if(trie[x][0]&&trie[x][1])
40     {
41         dfs(trie[x][0],i-1,now);
42 
43         dfs(trie[x][1],i-1,now);
44     } 
45 }
46 int main()
47 {
48     //freopen("da.in","r",stdin);
49     //freopen("ans.txt","w",stdout);
50     scanf("%d%d",&n,&m);
51     for(int i=1;i<=m;i++) scanf("%d",&a[i]);
52     for(int i=1;i<=m;i++) sum[i]=sum[i-1]^a[i];
53     int s=0;
54     for(int i=0;i<=m;i++)
55     {
56         int now=0;
57         a[i]=(2*a[i]/(1<<n)+2*a[i])%(1<<n);
58         s^=a[i];
59         now=s^sum[m]^sum[i];
60         insert(now);
61     }
62     dfs(0,n,0);
63     printf("%d\n%d",imax,mp[imax]);
64 }
View Code

40+0+16比较玄学分数,暴力打满100是rank7。

看了一下,现在rank51,非常危险的位置。

以前总觉得自己并不差,一次考不好下回可以考回来。

但是连续的排名靠后就暴露了问题。

正解接近但码不出来,暴力总在细节出错,思路明明接近却无法灵活变通。

非常致命。

如果连暴力都打不满,如果考试持续倒数,如果在NOIP就退役,这不是我想要的结果。

目标是停课,然后省队,然后拿政策。

我答应了的事情就要做到。

猜你喜欢

转载自www.cnblogs.com/MouDing/p/11286974.html