Noip模拟5 2021.6.7
T1 string(线段树优化)
看到数据范围就必须要想到优化,那么如何把26×M∗N 的复杂度降低呢??
用到那个我们最不想打的数据结构——线段树。。。。。。
然而,这个线段树与往常不同,他只需要用lazy下标,这样的话,也会比往常的码量稍微小点
(听别人说,这提可能还需要卡常)
不说了,代码表示比较明白
1 #include<bits/stdc++.h> 2 #define lid (id<<1) 3 #define rid (id<<1|1) 4 using namespace std; 5 inline int read(){ 6 int x=0,f=1; char ch=getchar(); 7 while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } 8 while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } 9 return x*f; 10 } 11 int n,m,summ[27];char s[100005]; 12 struct tree{ 13 int l,r,sum,lazy; 14 }; tree t[100005<<2]; 15 inline void pushup(int id){ 16 if(t[lid].sum==t[rid].sum) t[id].sum=t[lid].sum; 17 else t[id].sum=-1; 18 } 19 20 inline void pushdown(int id){ 21 if(t[id].sum!=-1) t[lid].sum=t[rid].sum=t[id].sum; 22 } 23 24 inline void bt(int id,int l,int r){ 25 t[id].l=l; t[id].r=r; t[id].sum=-1; 26 if(l==r){ t[id].sum=s[l]-'a'; return; } 27 int mid=(l+r)>>1; 28 bt(lid,l,mid); 29 bt(rid,mid+1,r); 30 pushup(id); 31 } 32 33 inline void add(int id,int l,int r){ 34 if(t[id].l>=l&&t[id].r<=r){ 35 if(t[id].sum!=-1){ 36 summ[t[id].sum]+=t[id].r-t[id].l+1; 37 return; 38 } 39 } 40 pushdown(id); 41 int mid=(t[id].l+t[id].r)>>1; 42 if(l<=mid) add(lid,l,r); 43 if(mid+1<=r) add(rid,l,r); 44 } 45 46 inline void change(int id,int l,int r,int v){ 47 if(l>r) return; 48 if(t[id].l>=l&&t[id].r<=r){ t[id].sum=v; return;} 49 int mid=(t[id].l+t[id].r)>>1; 50 if(l<=mid) change(lid,l,r,v); 51 if(mid+1<=r) change(rid,l,r,v); 52 pushup(id); 53 } 54 55 inline void qu(int id){ 56 if(t[id].sum!=-1){ 57 for(int i=1;i<=t[id].r-t[id].l+1;i++) 58 putchar(t[id].sum+'a'); 59 return; 60 } 61 qu(lid); 62 qu(rid); 63 } 64 namespace WSN{ 65 inline int main(){ 66 n=read(),m=read(); 67 scanf("%s",s+1); 68 bt(1,1,n); 69 while(m--){ 70 memset(summ,0,sizeof(summ)); 71 int l=read(),r=read(),x=read(); 72 add(1,l,r); 73 if(x==1){ 74 int ll=l; 75 for(int i=0;i<=25;i++){ 76 change(1,ll,ll+summ[i]-1,i); 77 ll+=summ[i]; 78 } 79 } 80 if(x==0){ 81 int ll=l; 82 for(int i=25;i>=0;i--){ 83 change(1,ll,ll+summ[i]-1,i); 84 ll+=summ[i]; 85 } 86 } 87 } 88 qu(1); 89 return 0; 90 } 91 } 92 signed main(){return WSN::main();}
T2 Matrix(Dp)
首先说一下,当这个题我看到dp方程表示的含义时,就吐了。。。这什么鬼才想出来的??? %%cbx
dp[i][j]表示在扫描到第i列时右区间的左端点在第i列左侧的个数是j,数组反映方案数。
唉,这大长定语我身为语文弱者已经尽力了。。。
数组l,r分别表示左区间的右端点在1~l间的个数:右区间的左端点在1~r间的个数,相当于维护一个前缀和
先考虑左区间:
扫描过一列后,扫面下一列而l[i]==l[i-1],表示该列无新的扫描完的左区间: f[i][j]=f[i1-][j]
扫描过一列后,扫面下一列而l[i]!=l[i-1],表示该列有新的扫描完的左区间:
总共有 l[i]−l[i−1]个左区间需要安排 1 ,之前还空着的列有 i−j−l[i−1] 个,所以安排左区间的方案数为
$P^{l[i]-l[i-1]}_{i-j-l[i-1]}$
再考虑右区间:
j>0,则f[i][j]可以由f[i-1][j-1]转移而来。总共r[i]行,放了j-1行,还剩r[i]-(j-1)行:
$f [ i ][ j ]+=f [ i - 1 ][ j-1 ]*[r[i]-(j-1)]*P^{l[i]-l[i-1]}_{i-j-l[i-1]}$
1 #include<bits/stdc++.h> 2 #define int long long 3 using namespace std; 4 inline int read(){ 5 int x=0,f=1; char ch=getchar(); 6 while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } 7 while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } 8 return x*f; 9 } 10 const int NN=3002,p=998244353; 11 int n,m,l[NN],r[NN],f[NN][NN]; 12 namespace WSN{ 13 inline int main(){ 14 f[0][0]=1; 15 n=read(),m=read(); 16 for(int i=1;i<=n;i++){ 17 int L=read(),R=read(); 18 l[L]++,r[R]++; 19 } 20 for(int i=1;i<=m;i++){ 21 f[i][0]=f[i-1][0]; 22 l[i]+=l[i-1]; 23 r[i]+=r[i-1]; 24 for(int j=1;j<=i;j++) 25 f[i][j]=(f[i-1][j]+f[i-1][j-1]*(r[i]-(j-1))%p)%p; 26 for(int k=0;k<=i;k++) 27 for(int j=l[i-1];j<l[i];j++) 28 f[i][k]=f[i][k]*(i-j-k)%p; 29 } 30 printf("%lld\n",f[m][n]); 31 return 0; 32 } 33 } 34 signed main(){return WSN::main();}
T3 Big (trie树)
巧妙的将复杂的函数取模柿子转化为理解的懂得柿子:
前面的地板函数模完2^n后值域是0和1,后面加上2x并模一个2^n意思是向左移一位加起来就是相当于将原数的二进制首位推到末尾,其他位向前移动一位,举个不是二进制的例子
1234----->> 2341
这样,我们就可以明显的发现这是一道trie树题
我们以对手的操作为断点,前面会让x xor一些数,操作后也会xor,这样,我们可以记录前缀和,枚举对手操作的各种断点,这样就可以找到最优解了
再提一下的就是分情况
一节点有两个孩子,那么无论我们如何构造x,对手一定能将这一位变为0,所以答案这一位为0
如果只有一个儿子,那么我们一定可以构造出一个x,使答案的这一位为1,所以答案这一位为1。
所以一边记录最大值一边统计个数就完了
1 #include<bits/stdc++.h> 2 #define X ((x>>(i-1))&1) 3 using namespace std; 4 inline int read(){ 5 int x=0,f=1; char ch=getchar(); 6 while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } 7 while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } 8 return x*f; 9 } 10 const int NN=300005; 11 int n,m,ch[8000000][3],tot=1,a[NN],s[NN],ans1,ans2; 12 inline int change(int x){return (x>>(n-1))+((x&(~(1<<(n-1))))<<1);} 13 //attacker use problem idea to change the number 14 inline void insert(int x){ 15 int u=1; 16 for(int i=n;i>=1;i--){ 17 if(!ch[u][X]) ch[u][X]=++tot; 18 u=ch[u][X]; 19 } 20 } 21 inline void dfs(int u,int x,int len){ 22 if(len==0){ 23 if(x>ans1){ 24 ans1=x; 25 ans2=1; 26 }else if(x==ans1) ans2++; 27 return; 28 } 29 if(ch[u][0]&&ch[u][1]){ 30 dfs(ch[u][0],x,len-1); 31 dfs(ch[u][1],x,len-1); 32 } 33 if(ch[u][0]&&!ch[u][1]) dfs(ch[u][0],(x|(1<<(len-1))),len-1); 34 if(ch[u][1]&&!ch[u][0]) dfs(ch[u][1],(x|(1<<(len-1))),len-1); 35 } 36 namespace WSN{ 37 inline int main(){ 38 n=read(),m=read();//n mean the length of "2 jinzhi" 39 for(int i=1;i<=m;i++) a[i]=read(); 40 for(int i=1;i<=m;i++) s[i]=s[i-1]^a[i];//xor "qian z s[m]^s[i])); 41 for(int i=0;i<=m;i++) insert(change(s[i])^(s[m]^s[i])); 42 dfs(1,0,n); 43 printf("%d\n%d\n",ans1,ans2); 44 return 0; 45 } 46 } 47 signed main(){return WSN::main();}
END
这次考试还是有小问题,就是时间分配不均。
超级有自信的打过了T4,然而也花费了超级多的时间,结果是T4没有拿到满分,前面题也顾不上做,幸好前面的题整体得分不高,而我的第四题想的也是正解思路,总之不亏吧~~
再%%XIN考试全场首A第四题太强了

浙公网安备 33010602011771号