Warning: session_start(): open(/tmp/sess_443335e2a2eb66ff2a08b28473aeffc5, O_RDWR) failed: No space left on device (28) in /data/wiki/inc/init.php on line 239

Warning: session_start(): Failed to read session data: files (path: ) in /data/wiki/inc/init.php on line 239

Warning: Cannot modify header information - headers already sent by (output started at /data/wiki/inc/init.php:239) in /data/wiki/inc/auth.php on line 430

Warning: Cannot modify header information - headers already sent by (output started at /data/wiki/inc/init.php:239) in /data/wiki/inc/actions.php on line 38

Warning: Cannot modify header information - headers already sent by (output started at /data/wiki/inc/init.php:239) in /data/wiki/lib/tpl/dokuwiki/main.php on line 12
2020-2021:teams:legal_string:jxm2001:other:错题集_4 [CVBB ACM Team]

用户工具

站点工具


2020-2021:teams:legal_string:jxm2001:other:错题集_4

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
2020-2021:teams:legal_string:jxm2001:other:错题集_4 [2021/02/02 20:21]
jxm2001
2020-2021:teams:legal_string:jxm2001:other:错题集_4 [2021/02/28 19:08] (当前版本)
jxm2001
行 328: 行 328:
  while(!vis[pos])pos++;​  while(!vis[pos])pos++;​
  enter(pos);​  enter(pos);​
 + return 0;
 +}
 +</​code>​
 +</​hidden>​
 +
 +===== 5、GCD or MIN =====
 +
 +[[https://​atcoder.jp/​contests/​abc191/​tasks/​abc191_f|链接]]
 +
 +==== 题意 ====
 +
 +给定 $n$ 个数,每次可以任选两个数进行 $\text{gcd}$ 或 $\min$ 操作得到一个新数再删去原来两个数。不断进行操作直到只剩下一个数。
 +
 +问剩下的数一共有多少种可能的取值。
 +
 +==== 题解 ====
 +
 +首先不难发现剩下的数一定不大于 $\min (a)$。再通过观察不难发现答案等于所有不超过 $\min (a)$ 的仅通过 $\text{gcd}$ 操作可以得到的数。
 +
 +首先 $\min (a)$ 一定可以得到,下面仅考虑小于 $\min (a)$ 的数 $v$。
 +
 +记 $a$ 中所有满足 $v\mid a_i$ 的数为 $b_1,​b_2\cdots b_k$。不难发现 $v$ 可以得到当且仅当 $\text{gcd}(b_1,​b_2\cdots b_k)=v$。
 +
 +于是可以遍历 $a_i$。对每个 $a_i$,遍历他们的所有因子 $d$,同时更新 $g(d)$。($d$ 从 $1$ 开始)
 +
 +如果 $g(d)$ 不存在则将 $g(d)$ 设为 $a_i$,否则将 $g(d)$ 设为 $\text{gcd}(g(d),​a_i)$。
 +
 +最后答案为 $1+$ 所有满足 $g(d)==d$ 的 $d$ 的个数。时间复杂度 $O(n\sqrt v\log v)$。
 +
 +<hidden 查看代码>​
 +<code cpp>
 +const int MAXN=2005,​inf=1e9;​
 +map<​int,​int>​ g;
 +int gcd(int a,int b){
 + while(b){
 + int t=b;
 + b=a%b;
 + a=t;
 + }
 + return a;
 +}
 +void update(int idx,int v){
 + if(g.find(idx)!=g.end())
 + g[idx]=gcd(g[idx],​v);​
 + else
 + g[idx]=v;
 +}
 +int a[MAXN];
 +int main()
 +{
 + int n=read_int(),​minv=inf;​
 + _for(i,​0,​n)a[i]=read_int(),​minv=min(a[i],​minv);​
 + _for(i,​0,​n){
 + for(int j=1;​j*j<​=a[i]&&​j<​minv;​j++){
 + if(a[i]%j==0){
 + update(j,​a[i]);​
 + if(a[i]/​j<​minv)
 + update(a[i]/​j,​a[i]);​
 + }
 + }
 + }
 + int ans=1;
 + for(map<​int,​int>::​iterator it=g.begin();​it!=g.end();​++it){
 + if(it->​first==it->​second)
 + ans++;
 + }
 + enter(ans);​
 + return 0;
 +}
 +</​code>​
 +</​hidden>​
 +
 +===== 6、Minimum Difference =====
 +
 +[[https://​codeforces.com/​contest/​1476/​problem/​G|链接]]
 +
 +==== 题意 ====
 +
 +给定 $n$ 个位置,每个位置一个照明范围为 $p_i$ 的灯,如果该灯朝左,则照明区域为 $[i-p_i,​i-1]$,如果该灯朝右,则照明区域为 $[i+1,​i+p_i]$。
 +
 +判定是否可以给每个灯一个朝向,使得 $[1,n]$ 区域全被照明,同时输出合法方案。
 +
 +==== 题解 ====
 +
 +设 $\text{dp}(i)$ 表示前 $i$ 个灯的最大照明前缀。
 +
 +设 $\text{pre}(i)$ 如果为 $0$ 则表示第 $i$ 个灯没有约束,如果不为 $0$ 则表示第 $i$ 个灯强制朝左且第 $[\text{pre}(i)+1,​i-1]$ 灯贪心朝右。
 +
 +如果第 $i$ 个灯强制朝左,则考虑与之前的照明前缀拼接,于是需要找到 $j$ 满足 $\text{dp}(j)+1\ge i-p_i$。如果存在 $j$,则 $\text{dp}(i)$ 至少为 $i-1$。
 +
 +然后 $j+1\sim i-1$ 的所有灯可以贪心考虑全部朝右,于是可以选取 $\max_{j+1\le k\le i-1} (i+p_i)$ 更新 $\text{dp}(i)$,可以 $\text{ST}$ 表维护。
 +
 +另外如果 $\text{pre}(j)=0$,即 $j$ 没有强制向左的约束,还可以用 $j+p_j$ 更新 $\text{dp}(i)$。
 +
 +不难发现满足条件的 $j$ 越小 $\text{dp}(i)$ 越大,于是权值线段树维护满足条件的最小下标即可。
 +
 +如果第 $i$ 个灯朝右,首先 $\text{dp}(i)$ 至少为 $\text{dp}(i-1)$。如果 $\text{dp}(i-1)\ge i$,则 $\text{dp}(i)\gets i+p_i$。
 +
 +最后,如果强制朝左的收益大于朝右的收益,则强制朝左且 $\text{pre}(i)=j$,否则贪心朝右且 $\text{pre}(i)=0$。
 +
 +输出方案所有灯贪心朝右,遇到 $\text{pre}(pos)\neq 0$ 则把该灯改成朝左且跳转到 $\text{pre}(pos)$ 即可。
 +
 +<hidden 查看代码>​
 +<code cpp>
 +const int MAXN=3e5+5,​MAXM=20,​inf=1e9;​
 +namespace ST{
 + int d[MAXN][MAXM],​lg2[MAXN];​
 + void build(int *a,int n){
 + lg2[1]=0;
 + _rep(i,​2,​n)lg2[i]=lg2[i>>​1]+1;​
 + _rep(i,​1,​n)d[i][0]=a[i];​
 + for(int j=1;​(1<<​j)<​=n;​j++){
 + _rep(i,​1,​n-(1<<​j)+1)
 + d[i][j]=max(d[i][j-1],​d[i+(1<<​(j-1))][j-1]);​
 + }
 + }
 + int query(int lef,int rig){
 + int len=rig-lef+1;​
 + return max(d[lef][lg2[len]],​d[rig-(1<<​lg2[len])+1][lg2[len]]);​
 + }
 +};
 +int p[MAXN],​a[MAXN],​dp[MAXN],​pre[MAXN];​
 +char ans[MAXN];
 +int lef[MAXN<<​2],​rig[MAXN<<​2],​s[MAXN<<​2];​
 +void build(int k,int L,int R){
 + lef[k]=L,​rig[k]=R,​s[k]=inf;​
 + if(L==R)return;​
 + int M=L+R>>​1;​
 + build(k<<​1,​L,​M);​
 + build(k<<​1|1,​M+1,​R);​
 +}
 +void update(int k,int pos,int v){
 + s[k]=min(s[k],​v);​
 + if(lef[k]==rig[k])return;​
 + int mid=lef[k]+rig[k]>>​1;​
 + if(mid>​=pos)update(k<<​1,​pos,​v);​
 + else update(k<<​1|1,​pos,​v);​
 +}
 +int query(int k,int L,int R){
 + if(L<​=lef[k]&&​rig[k]<​=R)return s[k];
 + int mid=lef[k]+rig[k]>>​1;​
 + if(mid>​=R)return query(k<<​1,​L,​R);​
 + else if(mid<​L)return query(k<<​1|1,​L,​R);​
 + return min(query(k<<​1,​L,​R),​query(k<<​1|1,​L,​R));​
 +}
 +int main()
 +{
 + int T=read_int();​
 + while(T--){
 + int n=read_int();​
 + _rep(i,​1,​n)p[i]=read_int(),​a[i]=min(p[i]+i,​n);​
 + ST::​build(a,​n);​
 + build(1,​0,​n);​
 + _rep(i,​1,​n){
 + int last=query(1,​max(0,​i-p[i]-1),​n);​
 + if(last==inf){
 + dp[i]=dp[i-1];​
 + pre[i]=0;​
 + }
 + else{
 + int vl=i-1,​vr=dp[i-1];​
 + if(!pre[last])vl=max(vl,​ST::​query(last,​i-1));​
 + else if(last+1<​=i-1)vl=max(vl,​ST::​query(last+1,​i-1));​
 + if(dp[i-1]>​=i)vr=max(vr,​a[i]);​
 + if(vl<​=vr){
 + dp[i]=vr;​
 + pre[i]=0;​
 + }
 + else{
 + dp[i]=vl;​
 + pre[i]=last;​
 + }
 + }
 + update(1,​dp[i],​i);​
 + }
 + if(dp[n]<​n)puts("​NO"​);​
 + else{
 + puts("​YES"​);​
 + _rep(i,​1,​n)ans[i]='​R';​
 + ans[n+1]='​\0';​
 + int pos=n;
 + while(pos){
 + if(pre[pos])ans[pos]='​L',​pos=pre[pos];​
 + else
 + pos--;
 + }
 + puts(ans+1);​
 + }
 + }
 + return 0;
 +}
 +</​code>​
 +</​hidden>​
 +
 +===== 7、Make Them Similar =====
 +
 +[[https://​codeforces.com/​problemset/​problem/​1257/​F|链接]]
 +
 +==== 题意 ====
 +
 +给定一个长度为 $n$ 的序列 $a_1,​a_2\cdots a_n$,要求找到 $x$ 使得 $a_1\oplus x,a_2\oplus x\cdots a_n\oplus x$ 中的二进制表示的 $1$ 一样多。
 +
 +其中 $n\le 100,a_i\lt 2^{30}$。
 +
 +==== 题解 ====
 +
 +暴力枚举 $x$ 的低 $15$ 位和高 $15$ 位。假设当前枚举低 $15$ 位,且当前枚举的数为 $v$,记 $b_i$ 为 $a_i\oplus v$ 的二进制表示中的 $1$ 的个数。
 +
 +将 $b_2-b_1,​b_3-b_1\cdots b_n-b_1$ 所代表的序列插入字典树。然后枚举高位时查看有无刚好可以构成相反数的序列即可。
 +
 +时间复杂度 $O(n\sqrt v)$。
 +
 +<hidden 查看代码>​
 +<code cpp>
 +const int MAXN=105,​MAXL=15,​MAXV=1<<​MAXL,​MAXS=MAXN*MAXV;​
 +int n,​a[MAXN],​b[MAXN];​
 +int cal(int v){
 + int cnt=0;
 + while(v){
 + cnt+=v&​1;​
 + v>>​=1;​
 + }
 + return cnt;
 +}
 +int ch[MAXS][MAXL<<​1|1],​val[MAXS],​cnt;​
 +void Insert(int v){
 + int pos=0;
 + _for(i,​1,​n){
 + if(!ch[pos][b[i]])
 + ch[pos][b[i]]=++cnt;​
 + pos=ch[pos][b[i]];​
 + }
 + val[pos]=v;​
 +}
 +void query(int v){
 + int pos=0;
 + _for(i,​1,​n){
 + if(!ch[pos][b[i]])return;​
 + pos=ch[pos][b[i]];​
 + }
 + enter((v<<​15)|val[pos]);​
 + exit(0);
 +}
 +int main()
 +{
 + n=read_int();​
 + _for(i,​0,​n)a[i]=read_int();​
 + _for(i,​0,​MAXV){
 + _for(j,​0,​n)
 + b[j]=cal((a[j]&​(MAXV-1))^i);​
 + _for(j,​1,​n)
 + b[j]=b[j]-b[0]+MAXL;​
 + Insert(i);​
 + }
 + _for(i,​0,​MAXV){
 + _for(j,​0,​n)
 + b[j]=cal((a[j]>>​15)^i);​
 + _for(j,​1,​n)
 + b[j]=b[0]-b[j]+MAXL;​
 + query(i);
 + }
 + puts("​-1"​);​
  return 0;  return 0;
 } }
 </​code>​ </​code>​
 </​hidden>​ </​hidden>​
2020-2021/teams/legal_string/jxm2001/other/错题集_4.1612268518.txt.gz · 最后更改: 2021/02/02 20:21 由 jxm2001