8.1题解
下午有点不舒服,这个人状态都不好了,一道破题,两个zz错误搞了一下午,刚刚水了道题,几年前的NOIP真水
T1
怎么说呢,暴力40,跟排序那道题有点像,线段树维护01串,二分答案,不过这道题,某Larry实测,n遍二分,T到飞,正解嘛。着实和那道题有点像,那道题是线段树维护1的个数,也就是区间和,排序通过区间覆盖实现,这道题的话,线段树维护每个区间中26个字母出现的次数,dalao讲题的时候说用桶,我无比懵逼,结果不就是数组维护字母出现的个数嘛,说的倒挺高大上,可能还是我太弱了叭,然后进行覆盖,之后输出的时候搜到底输出即可,不过出题人卡常真心恶心,正解T90?for循环展开就A了,实属稍恶心
1 #include<iostream> 2 #include<cstdio> 3 #define maxn 100100 4 #define maxx 28 5 using namespace std; 6 struct node{ 7 int zuo,you; 8 int tong[maxx],lan; 9 }s[maxn*4],cun; 10 int n,m; 11 int a[maxn]; 12 char aa[maxn]; 13 inline void mem(int f) 14 { 15 for(int i=1;i<=26;++i) s[f].tong[i]=0; 16 } 17 inline void up(int f) 18 { 19 for(int i=1;i<=26;++i) s[f].tong[i]=s[2*f].tong[i]+s[2*f+1].tong[i]; 20 } 21 inline void down(int f) 22 { 23 if(s[f].zuo==s[f].you) return ; 24 if(s[f].lan) 25 { 26 int ls=s[f].lan; 27 mem(2*f); mem(2*f+1); 28 s[2*f].tong[ls]=s[2*f].you-s[2*f].zuo+1; 29 s[2*f+1].tong[ls]=s[2*f+1].you-s[2*f+1].zuo+1; 30 s[2*f].lan=ls; s[2*f+1].lan=ls; s[f].lan=0; 31 } 32 } 33 inline void build(int f,int l,int r) 34 { 35 s[f].zuo=l; s[f].you=r; 36 if(l==r) {s[f].tong[a[l]]++; return ;} 37 int mid=(s[f].zuo+s[f].you)>>1; 38 build(2*f,l,mid); build(2*f+1,mid+1,r); 39 up(f); 40 } 41 inline void quary(int f,int l,int r) 42 { 43 if(s[f].zuo>=l&&s[f].you<=r) 44 { 45 for(int i=1;i<=26;++i) cun.tong[i]+=s[f].tong[i]; 46 return ; 47 } 48 down(f); 49 int mid=(s[f].zuo+s[f].you)>>1; 50 if(l<=mid) quary(2*f,l,r); 51 if(r>mid) quary(2*f+1,l,r); 52 } 53 inline void change(int f,int l,int r,int w) 54 { 55 if(s[f].zuo>=l&&s[f].you<=r) 56 { 57 mem(f); s[f].tong[w]=s[f].you-s[f].zuo+1; 58 s[f].lan=w; return ; 59 } 60 down(f); 61 int mid=(s[f].zuo+s[f].you)>>1; 62 if(l<=mid) change(2*f,l,r,w); 63 if(r>mid) change(2*f+1,l,r,w); 64 up(f); 65 } 66 inline void out(int f) 67 { 68 char ans; 69 if(s[f].zuo==s[f].you) 70 { 71 for(int i=1;i<=26;++i) 72 if(s[f].tong[i]) ans=i-1+'a'; 73 printf("%c",ans); 74 return ; 75 } 76 down(f); out(2*f); out(2*f+1); 77 } 78 int main() 79 { 80 scanf("%d%d",&n,&m); scanf("%s",aa+1); 81 for(int i=1;i<=n;++i) a[i]=aa[i]-'a'+1; 82 build(1,1,n); 83 for(int i=1;i<=m;++i) 84 { 85 int l,r,x; scanf("%d%d%d",&l,&r,&x); 86 for(int i=1;i<=26;++i) cun.tong[i]=0; 87 quary(1,l,r); 88 if(x==0) 89 { 90 int js=l; 91 for(int i=26;i>=1;--i) 92 if(cun.tong[i]) {change(1,js,js+cun.tong[i]-1,i); js+=cun.tong[i];} 93 } 94 else 95 { 96 int js=l; 97 for(int i=1;i<=26;++i) 98 if(cun.tong[i]) {change(1,js,js+cun.tong[i]-1,i); js+=cun.tong[i];} 99 } 100 } 101 out(1); puts(""); 102 return 0; 103 }
1 #include<iostream> 2 #include<cstdio> 3 #define maxn 100100 4 #define maxx 28 5 using namespace std; 6 struct node{ 7 int zuo,you; 8 int tong[maxx],lan; 9 }s[maxn*4],cun; 10 int n,m; 11 int a[maxn]; 12 char aa[maxn]; 13 inline void mem(int f) 14 { 15 s[f].tong[1]=0; s[f].tong[2]=0; s[f].tong[3]=0; 16 s[f].tong[4]=0; s[f].tong[5]=0; s[f].tong[6]=0; 17 s[f].tong[7]=0; s[f].tong[8]=0; s[f].tong[9]=0; 18 s[f].tong[10]=0; s[f].tong[11]=0; s[f].tong[12]=0; 19 s[f].tong[13]=0; s[f].tong[14]=0; s[f].tong[15]=0; 20 s[f].tong[16]=0; s[f].tong[17]=0; s[f].tong[18]=0; 21 s[f].tong[19]=0; s[f].tong[20]=0; s[f].tong[21]=0; 22 s[f].tong[22]=0; s[f].tong[23]=0; s[f].tong[24]=0; 23 s[f].tong[25]=0; s[f].tong[26]=0; 24 } 25 inline void up(int f) 26 { 27 s[f].tong[1]=s[2*f].tong[1]+s[2*f+1].tong[1]; 28 s[f].tong[2]=s[2*f].tong[2]+s[2*f+1].tong[2]; 29 s[f].tong[3]=s[2*f].tong[3]+s[2*f+1].tong[3]; 30 s[f].tong[4]=s[2*f].tong[4]+s[2*f+1].tong[4]; 31 s[f].tong[5]=s[2*f].tong[5]+s[2*f+1].tong[5]; 32 s[f].tong[6]=s[2*f].tong[6]+s[2*f+1].tong[6]; 33 s[f].tong[7]=s[2*f].tong[7]+s[2*f+1].tong[7]; 34 s[f].tong[8]=s[2*f].tong[8]+s[2*f+1].tong[8]; 35 s[f].tong[9]=s[2*f].tong[9]+s[2*f+1].tong[9]; 36 s[f].tong[10]=s[2*f].tong[10]+s[2*f+1].tong[10]; 37 s[f].tong[11]=s[2*f].tong[11]+s[2*f+1].tong[11]; 38 s[f].tong[12]=s[2*f].tong[12]+s[2*f+1].tong[12]; 39 s[f].tong[13]=s[2*f].tong[13]+s[2*f+1].tong[13]; 40 s[f].tong[14]=s[2*f].tong[14]+s[2*f+1].tong[14]; 41 s[f].tong[15]=s[2*f].tong[15]+s[2*f+1].tong[15]; 42 s[f].tong[16]=s[2*f].tong[16]+s[2*f+1].tong[16]; 43 s[f].tong[17]=s[2*f].tong[17]+s[2*f+1].tong[17]; 44 s[f].tong[18]=s[2*f].tong[18]+s[2*f+1].tong[18]; 45 s[f].tong[19]=s[2*f].tong[19]+s[2*f+1].tong[19]; 46 s[f].tong[20]=s[2*f].tong[20]+s[2*f+1].tong[20]; 47 s[f].tong[21]=s[2*f].tong[21]+s[2*f+1].tong[21]; 48 s[f].tong[22]=s[2*f].tong[22]+s[2*f+1].tong[22]; 49 s[f].tong[23]=s[2*f].tong[23]+s[2*f+1].tong[23]; 50 s[f].tong[24]=s[2*f].tong[24]+s[2*f+1].tong[24]; 51 s[f].tong[25]=s[2*f].tong[25]+s[2*f+1].tong[25]; 52 s[f].tong[26]=s[2*f].tong[26]+s[2*f+1].tong[26]; 53 } 54 inline void down(int f) 55 { 56 if(s[f].zuo==s[f].you) return ; 57 if(s[f].lan) 58 { 59 int ls=s[f].lan; 60 mem(2*f); mem(2*f+1); 61 s[2*f].tong[ls]=s[2*f].you-s[2*f].zuo+1; 62 s[2*f+1].tong[ls]=s[2*f+1].you-s[2*f+1].zuo+1; 63 s[2*f].lan=ls; s[2*f+1].lan=ls; s[f].lan=0; 64 } 65 } 66 inline void build(int f,int l,int r) 67 { 68 s[f].zuo=l; s[f].you=r; 69 if(l==r) {s[f].tong[a[l]]++; return ;} 70 int mid=(s[f].zuo+s[f].you)>>1; 71 build(2*f,l,mid); build(2*f+1,mid+1,r); 72 up(f); 73 } 74 inline void quary(int f,int l,int r) 75 { 76 if(s[f].zuo>=l&&s[f].you<=r) 77 { 78 cun.tong[1]+=s[f].tong[1]; 79 cun.tong[2]+=s[f].tong[2]; 80 cun.tong[3]+=s[f].tong[3]; 81 cun.tong[4]+=s[f].tong[4]; 82 cun.tong[5]+=s[f].tong[5]; 83 cun.tong[6]+=s[f].tong[6]; 84 cun.tong[7]+=s[f].tong[7]; 85 cun.tong[8]+=s[f].tong[8]; 86 cun.tong[9]+=s[f].tong[9]; 87 cun.tong[10]+=s[f].tong[10]; 88 cun.tong[11]+=s[f].tong[11]; 89 cun.tong[12]+=s[f].tong[12]; 90 cun.tong[13]+=s[f].tong[13]; 91 cun.tong[14]+=s[f].tong[14]; 92 cun.tong[15]+=s[f].tong[15]; 93 cun.tong[16]+=s[f].tong[16]; 94 cun.tong[17]+=s[f].tong[17]; 95 cun.tong[18]+=s[f].tong[18]; 96 cun.tong[19]+=s[f].tong[19]; 97 cun.tong[20]+=s[f].tong[20]; 98 cun.tong[21]+=s[f].tong[21]; 99 cun.tong[22]+=s[f].tong[22]; 100 cun.tong[23]+=s[f].tong[23]; 101 cun.tong[24]+=s[f].tong[24]; 102 cun.tong[25]+=s[f].tong[25]; 103 cun.tong[26]+=s[f].tong[26]; 104 return ; 105 } 106 down(f); 107 int mid=(s[f].zuo+s[f].you)>>1; 108 if(l<=mid) quary(2*f,l,r); 109 if(r>mid) quary(2*f+1,l,r); 110 } 111 inline void change(int f,int l,int r,int w) 112 { 113 if(s[f].zuo>=l&&s[f].you<=r) 114 { 115 mem(f); s[f].tong[w]=s[f].you-s[f].zuo+1; 116 s[f].lan=w; return ; 117 } 118 down(f); 119 int mid=(s[f].zuo+s[f].you)>>1; 120 if(l<=mid) change(2*f,l,r,w); 121 if(r>mid) change(2*f+1,l,r,w); 122 up(f); 123 } 124 inline void out(int f) 125 { 126 char ans; 127 if(s[f].zuo==s[f].you) 128 { 129 if(s[f].tong[1]) ans=1-1+'a'; 130 if(s[f].tong[2]) ans=2-1+'a'; 131 if(s[f].tong[3]) ans=3-1+'a'; 132 if(s[f].tong[4]) ans=4-1+'a'; 133 if(s[f].tong[5]) ans=5-1+'a'; 134 if(s[f].tong[6]) ans=6-1+'a'; 135 if(s[f].tong[7]) ans=7-1+'a'; 136 if(s[f].tong[8]) ans=8-1+'a'; 137 if(s[f].tong[9]) ans=9-1+'a'; 138 if(s[f].tong[10]) ans=10-1+'a'; 139 if(s[f].tong[11]) ans=11-1+'a'; 140 if(s[f].tong[12]) ans=12-1+'a'; 141 if(s[f].tong[13]) ans=13-1+'a'; 142 if(s[f].tong[14]) ans=14-1+'a'; 143 if(s[f].tong[15]) ans=15-1+'a'; 144 if(s[f].tong[16]) ans=16-1+'a'; 145 if(s[f].tong[17]) ans=17-1+'a'; 146 if(s[f].tong[18]) ans=18-1+'a'; 147 if(s[f].tong[19]) ans=19-1+'a'; 148 if(s[f].tong[20]) ans=20-1+'a'; 149 if(s[f].tong[21]) ans=21-1+'a'; 150 if(s[f].tong[22]) ans=22-1+'a'; 151 if(s[f].tong[23]) ans=23-1+'a'; 152 if(s[f].tong[24]) ans=24-1+'a'; 153 if(s[f].tong[25]) ans=25-1+'a'; 154 if(s[f].tong[26]) ans=26-1+'a'; 155 printf("%c",ans); 156 return ; 157 } 158 down(f); out(2*f); out(2*f+1); 159 } 160 int main() 161 { 162 scanf("%d%d",&n,&m); scanf("%s",aa+1); 163 for(int i=1;i<=n;++i) a[i]=aa[i]-'a'+1; 164 build(1,1,n); 165 for(int i=1;i<=m;++i) 166 { 167 int l,r,x; scanf("%d%d%d",&l,&r,&x); 168 for(int i=1;i<=26;++i) cun.tong[i]=0; 169 quary(1,l,r); 170 if(x==0) 171 { 172 int js=l; 173 for(int i=26;i>=1;--i) 174 if(cun.tong[i]) {change(1,js,js+cun.tong[i]-1,i); js+=cun.tong[i];} 175 } 176 else 177 { 178 int js=l; 179 for(int i=1;i<=26;++i) 180 if(cun.tong[i]) {change(1,js,js+cun.tong[i]-1,i); js+=cun.tong[i];} 181 } 182 } 183 out(1); puts(""); 184 return 0; 185 }
那个懒标记我一开始记麻烦了,就不误导大家了,不过在线段树中懒标记确实很重要,做题的时候最好仔细思考一下懒标记为什么这么记,以及这么记带来的效果和便利是什么
T2
一道脑回路清奇的dp题,是道拓宽思路的好题,光dp定义,我就理解了很久,wjn考场AC,属实nb,上题解
设$dp[i][j]$代表当前处理到第$i$列,有$j$个右区间的左端点在第$i$列及其以左,放置了1,也就是放了$j$个1,不过是右区间中的$j$个,要不换个说法?有$j$个右区间在第$i$列及其以左放了1,emm大概是这样了,这样的话dp转移就由传统的按行或按格转移,变为了按列转移,状态转移的话我们先考虑那个乱入的右区间
$do[i][j]+=dp[i-1][j]$
这个就是不放一,很好理解
$do[i][j]+=dp[i-1][j-1]*[r[i]-(j-1)]$
补充数组定义,$l[i]$表示左区间在$i$及$i$列以左的区间总数,也就是前缀和,$r[i]$同理,代表右区间在$i$列及其以左的区间总数,依旧前缀和,这个都计算$i$及其以左的前缀和的原因其实还挺好理解的,毕竟你在从左向右转移嘛,$dp[i-1][j-1]$就是要在前i列再放一个1,$r[i]-(j-1)$就是现在可以放一的区间个数
所以就是一个放1,一个不放1两种情况
接下来处理左区间,先上式子吧,$dp[i][j]=dp[i][j]*A_{i-j-l[i-1]}^{l[i]-l[i-1]}$,关于这个排列数,其实就是你现在一共有$i-j-l[i-1]$个位置可以放1,而你需要放$l[i]-l[i-1]$个一,那为什么不是组合数是排列数呢?举个例子,一共要选$x$列放1,其中对于第$p$行和第$q$行,你都在第$o$列放一,当然不是同一种情况,感性理解一下,当然就是排列数
这道题很拓宽思路,以后又可以有一种新的思路
1 #include<iostream> 2 #include<cstdio> 3 #define maxn 3010 4 #define mod 998244353 5 #define int long long 6 using namespace std; 7 int n,m; 8 int zuo[maxn]/*左区间端点个数前缀和*/,you[maxn]/*右区间端点个数前缀和*/,jc[maxn],ny[maxn]; 9 int f[maxn][maxn];//右区间的左端点在第i列及其以左,已经放了j个1 10 int ksm(int a,int b,int c) 11 { 12 int ans=1; a=a%mod; 13 while(b) 14 { 15 if(b&1) ans=(ans*a)%mod; 16 b=b>>1; a=(a*a)%mod; 17 } 18 return ans%mod; 19 } 20 int A(int x,int y) 21 { 22 if(x<0) return 1ll*0; 23 if(y<0) return 1ll*0; 24 if(x<y) return 1ll*0; 25 if(y==0) return 1ll*1; 26 return (jc[x]*ny[x-y])%mod; 27 } 28 signed main() 29 { 30 scanf("%lld%lld",&n,&m); 31 jc[0]=1; 32 int ls=max(n,m); 33 for(int i=1;i<=ls;++i) jc[i]=(jc[i-1]*i)%mod; 34 ny[ls]=ksm(jc[ls],mod-2,mod); 35 for(int i=ls;i>=1;--i) ny[i-1]=(ny[i]*i)%mod; 36 for(int i=1;i<=n;++i) 37 { 38 int l,r; scanf("%lld%lld",&l,&r); 39 zuo[l]++; you[r]++; 40 } 41 if(2*n>m) {printf("0\n"); return 0;} 42 for(int i=1;i<=m;++i) {zuo[i]+=zuo[i-1]; you[i]+=you[i-1];} 43 f[0][0]=1; 44 for(int i=1;i<=m;++i) 45 { 46 for(int j=0;j<=min(i,n);++j) 47 { 48 f[i][j]=(f[i][j]+f[i-1][j])%mod; 49 if(j>0) f[i][j]=(f[i][j]+f[i-1][j-1]*(you[i]-j+1))%mod; 50 f[i][j]=(f[i][j]*A(i-j-zuo[i-1],zuo[i]-zuo[i-1]))%mod; 51 } 52 } 53 printf("%lld\n",f[m][n]); 54 return 0; 55 }
T3
那个稍长的式子实际上是循环左移(不知道的问度娘吧)?是我见不多识不广了,那么对于异或来说,移位的影响其实很好解决,毕竟你不管是先移位再$xor$还是先$xor$再移位都可以得到正确答案,那对于中途$xor$一部分之后移位再继续$xor$,完全可以提前处理出来已知$a$数组的不同异或和,这样的话我们就可以得到$m-1$个不同的异或和,你的任务是选一个和$[0,2^n)$之间的数异或值最大,那我们完全可以把这些01串放到$trie$树上去跑,当然题目中那句对手会让结果尽量小也很重要,他提醒我们要计算最小值的最大值,对于某一位来说如果$trie$树上既有0又有1,那这一位一定无法作出贡献,如果两者只有其一,那一定会作出贡献,所以建出$trie$树之后,$dfs$跑一下就可以了
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 #define int long long 5 #define maxm 100100 6 #define maxx 32 7 using namespace std; 8 int n,m,tot,Ans,j; 9 int a[maxm],deep[maxx*maxm]; 10 int trie[maxm*maxx][3]; 11 void jian(int x) 12 { 13 int p=0; 14 for(int k=n-1;k>=0;--k) 15 { 16 int ch=(x>>k)&1; 17 if(trie[p][ch]==0) {trie[p][ch]=++tot; deep[trie[p][ch]]=k;} 18 p=trie[p][ch]; 19 } 20 } 21 void dfs(int x,int w) 22 { 23 if(trie[x][0]&&trie[x][1]) {dfs(trie[x][0],w); dfs(trie[x][1],w);} 24 else if(!trie[x][0]&&trie[x][1]) dfs(trie[x][1],w+(1<<deep[trie[x][1]])); 25 else if(trie[x][0]&&!trie[x][1]) dfs(trie[x][0],w+(1<<deep[trie[x][0]])); 26 else if(!trie[x][0]&&!trie[x][1]) 27 { 28 if(w==Ans) j++; 29 else if(w>Ans) {Ans=w; j=1;} 30 return ; 31 } 32 } 33 signed main() 34 { 35 scanf("%lld%lld",&n,&m); 36 for(int i=1;i<=m;++i) {int x; scanf("%lld",&x); a[i]=a[i-1]^x;} 37 for(int i=0;i<=m;++i) 38 { 39 int ls=1<<n; 40 int ans=((2*a[i])/ls+2*a[i])%ls; 41 ans=ans^a[m]^a[i]; jian(ans); 42 } 43 dfs(0,0); 44 cout<<Ans<<endl<<j<<endl; 45 return 0; 46 }