2022 ICPC网络预选赛1

A
最难的一点就是没有处理环形后效性
比如询问L=1R=9 ->111000111应该是长度为6的连续一段1,贡献为6/2=3而不是2+2=4
做法就是预处理L右边最近的0的下标x,预处理R左边最近的0的下标y,然后把一段区间分成三份求解,中间就直接贡献sum[y]-sum[x-1]
然后把左边和右边连续的1加起来除于2
做法1,dp处理前缀和贡献
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
   | #include<bits/stdc++.h> using namespace std; const int N=1e6+10; char s[N]; int n,m,sum[N],L[N],R[N]; int f[N],l,r; signed main(){     ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);     cin>>n>>m>>s+1;     int cnt=0;     for(int i=1;i<=n;i++){         if(s[i]=='1')cnt++;         else cnt=0;         f[i]=f[i-cnt-1]+(cnt+1)/2;     }     L[0]=0;     for(int i=1;i<=n;i++){         if(s[i]=='1')L[i]=L[i-1];         else L[i]=i;     }     R[n+1]=n+1;     for(int i=n;i>=1;i--){         if(s[i]=='1')R[i]=R[i+1];         else R[i]=i;     }     while(m--){         cin>>l>>r;         int len=r-l+1;         int cnt1=r-L[r]+R[l]-l;         int t=f[L[r]]-f[R[l]-1]+(cnt1+1)/2;         cout<<max(0,len/3-t)<<'\n';     } }
 
   | 
 
做法2,对一段连续的1采取贡献差分,也就是+1+0+1+0+1+0,然后前缀和
这样可以保证奇数的时候上取整,偶数下取整
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
   | #include<bits/stdc++.h> using namespace std; const int N=1e6+10; char s[N]; int n,m,sum[N],L[N],R[N]; int cal(int l,int r){     int ll=R[l];     int rr=L[r];     if(ll>=rr)return (r-l+1)/2;     int len1=(r-rr)+(ll-l);     return sum[rr]-sum[ll-1]+(len1+1)/2; } signed main(){     ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);     cin>>n>>m>>s+1;                         int last=-1;     for(int i = 1; i <= n ; i ++ )      {         if(s[i] == '1')         {             if(last != i - 1) sum[i] = 1, last = i;             else last = -1;         }         sum[i] += sum[i - 1];     }     for(int i=1;i<=n;i++){         if(s[i]=='0')L[i]=i;         else L[i]=L[i-1];     }     R[n+1]=n+1;     for(int i=n;i>=1;i--){         if(s[i]=='0')R[i]=i;         else R[i]=R[i+1];     }     while(m--){         int l,r;         cin>>l>>r;         int len=r-l+1;         int t=cal(l,r);         cout<<max(0,len/3-t)<<'\n';     } }
   | 
 
B
先预处理我要选x个tower的状态,pushback进vector里面
开一个优先队列对一个个时间片,先拿出来,t=t+a[i],然后放进去
dfs爆搜
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
   | #include<bits/stdc++.h> using namespace std; const int N=16,M=(1<<N)+10; int n,a[N],b[N],ans=1e9; vector<int>v[M]; typedef pair<int,int>PII; priority_queue<PII,vector<PII>,greater<PII>>q; #define t first #define u second int cal(int x){     int ans=0;     for(int i=0;i<n;i++)ans+=x>>i&1;     return ans; } int dfs(int s,int T){     if(s+1==1<<n)return T;     int sum=0;     T=q.top().t;     while(q.size()&&q.top().t==T){         int u=q.top().u;         int t=q.top().t;         sum+=b[u];         q.pop();         q.push({T+a[u],u});     }     if(sum+cal(s)>=n)return T;     int ans=1e9;     for(auto x:v[sum]){         if(x&s)continue;         for(int i=0;i<n;i++){             if(x>>i&1)q.push({a[i]+T,i});         }         ans=min(ans,dfs(s|x,T));     }     return ans; } signed main(){     cin>>n;     for(int i=0;i<1<<n;i++){         v[cal(i)].push_back(i);     }     for(int i=0;i<n;i++)cin>>a[i]>>b[i];     for(int i=0;i<n;i++){         while(q.size())q.pop();         q.push({a[i],i});         ans=min(ans,dfs(1<<i,0));     }     cout<<ans; }
   | 
 
C
结论题,去理解题意,感受一下为什么
没有特判n==1wa了一发,没有清空度数数组wa了一发
D
我写的爆搜要剪很多枝才能刚好冲过去,vp的时候一直超时,改完就一发a了
而且代码很丑
说白了就是枚举长度len,枚举后缀零个数x,然后放一个1去挡,然后最前面也是一个1,dfs爆搜放y-2个1的情况,对询问二分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
   | #include<bits/stdc++.h> using namespace std; set<int>S; int T,l,r,len; int a[31];
  void dfs(int l,int r,int y){     if(l>r){         if(y==0){             int x=0;             for(int i=0;i<len;i++)x+=a[i]<<i;             S.insert(x);             return;         }         else return;     }          if(y==0){dfs(r+1,r,y);return;}     if(y==r-l+1){         for(int i=l;i<=r;i++)a[i]=1;         dfs(r+1,r,0);         for(int i=l;i<=r;i++)a[i]=0;         return;     }     if(y>r-l+1)return;     if(y)a[l]=1,dfs(l+1,r,y-1);     a[l]=0,dfs(l+1,r,y); } signed main(){     ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);     S.insert(2);     for(len=4;len<=30;len++){         for(int i=0;i<len;i++)a[i]=0;         for(int x=2;x<=len/2;x++){             for(int i=0;i<x;i++)a[i]=0;             a[x]=1;             a[len-1]=1;             int y=x-2;             dfs(x+1,len-2,y);         }     }     cin>>T;     while(T--){         cin>>l>>r;         auto it=S.lower_bound(l);         if(it==S.end()||*it>r)cout<<"-1"<<'\n';         else cout<<*it<<'\n';     } }
   | 
 
看到有个人写了个很优美的折半搜索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
   | #include<bits/stdc++.h> using namespace std; set<int>S; int T,l,r,len; int a[31]; void dfs(int x,int cnt,int p){     if(cnt==0){         if(x<=1e9)S.insert(x);         return;     }     if(p==30)return;     dfs(x|1<<p,cnt-1,p+1);     dfs(x,cnt,p+1); } signed main(){     ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);     for(int i=1;i<=16;i++)dfs(1<<i,i-1,i+1);     cin>>T;     while(T--){         cin>>l>>r;         auto it=S.lower_bound(l);         if(it==S.end()||*it>r)cout<<"-1"<<'\n';         else cout<<*it<<'\n';     } }
   | 
 
G
没读题
感觉读懂硬搞能写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
   | #include <bits/stdc++.h> #define rep(i, a, b) for(int i = (a); i < (b); i++) #define _for(i, a, b) for(int i = (a); i <= (b); i++) using namespace std;   typedef long long ll; const int N = 110; ll dp[6][55][35][27][22], k[5], x[5], s[N]; int n, T;   int main() {      scanf("%d%d", &n, &T);     _for(i, 1, 4)     {         scanf("%lld", &x[i]);         k[i] = k[i - 1] + x[i];     }     _for(i, 1, n)     {         ll x; scanf("%lld", &x);         s[i] = s[i - 1] + x;     }       ll ans = 0;     _for(i, 1, n)         _for(p1, 0, n / 2 + 1)              _for(p2, 0, n / 3 + 1)                 _for(p3, 0, n / 4 + 1)                     _for(p4, 0, n / 5 + 1)                     {                         if(p1 * k[1] + p2 * k[2] + p3 * k[3] + p4 * k[4] > T) break;                           ll res = dp[(i - 1 + 6) % 6][p1][p2][p3][p4];                         if(p1 - 1 >= 0 && i - 2 >= -1) res = max(res, dp[(i - 2 + 6) % 6][p1 - 1][p2][p3][p4] + s[i] - s[i - 1]);                         if(p2 - 1 >= 0 && i - 3 >= -1) res = max(res, dp[(i - 3 + 6) % 6][p1][p2 - 1][p3][p4] + s[i] - s[i - 2]);                         if(p3 - 1 >= 0 && i - 4 >= -1) res = max(res, dp[(i - 4 + 6) % 6][p1][p2][p3 - 1][p4] + s[i] - s[i - 3]);                         if(p4 - 1 >= 0 && i - 5 >= -1) res = max(res, dp[(i - 5 + 6) % 6][p1][p2][p3][p4 - 1] + s[i] - s[i - 4]);                           dp[i % 6][p1][p2][p3][p4] = res;                         if(i == n) ans = max(ans, dp[i % 6][p1][p2][p3][p4]);                     }     printf("%lld\n", ans);       return 0; }
   | 
 
H
赛时沛桐过的,写个递归就好了,如果紧张的话很大概率写不好
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
   | #include<bits/stdc++.h> using namespace std; #define int long long const int mod=20220911; int ans; int dfs(){     int ans=0;     string s;     while(cin>>s&&s!="for"){         if(s[0]=='l')ans++;         else if(s[0]=='r')ans+=dfs();     }     int x;     cin>>x;     return x*ans%mod; } signed main(){     ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);     string s;     while(cin>>s&&s!="fin"){         if(s[0]=='l')ans++;         else if(s[0]=='r')ans+=dfs();         ans%=mod;     }     cout<<ans; }
   | 
 
J
非常好的期望题
根据样例猜构造方式,需要刷题量足够多足够敏感
设第一个为A,然后想到错位相消就很快了
记得特判算出来的A不合法的情况和超出范围的情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
   | #include<bits/stdc++.h> #define int long long using namespace std; int x,y,s,A,a,b,n,m; signed main(){     cin>>x>>y;     for(int i=2;i<x;i++)s+=i;     for(m=2;;m++){         n=m-x+3;         if(n<=1||n>m)continue;         a=m*y-n*x+x-s;         b=m-s-n*x+x;         int d=__gcd(a,b);         a/=d;         b/=d;         if(a*b<=0||a>b)continue;         if(a>10000||b>10000)continue;         cout<<a<<" "<<b<<'\n';         for(int i=m;i>=n;i--)cout<<1<<" "<<i<<'\n';         cout<<"1 1"<<'\n';         return 0;     } }
   | 
 
L
赛时无法解决操作的前缀限制关系,即app,ppa会对应不同的方案,然后就不会了
结果发现题解是预处理了一个限制数组
,理解不是特别困难但就是想不到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
   | #include<bits/stdc++.h> using namespace std; const int N=5e5+10; int f[N][30],vis[30],dont[30][30],n,m; char a[N],b[N]; signed main(){     ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);     cin>>a+1>>b+1;     n=strlen(a+1);     m=strlen(b+1);     for(int i=1;i<=m;i++){         for(int j=0;j<26;j++){             if(vis[j])dont[j][b[i]-'a']=1;         }         vis[b[i]-'a']=1;     }     for(int i=1;i<=n;i++)f[i][a[i]-'a']=1;     for(int i=1;i<=n;i++){         int x=a[i]-'a';         if(!vis[x]){             for(int j=0;j<26;j++)f[i][j]=f[i-1][j]+1;         }         else{             for(int j=0;j<26;j++){                 f[i][j]=max(f[i][j],f[i-1][j]);                 if(dont[j][x])continue;                 f[i][x]=max(f[i][x],f[i-1][j]+1);             }         }     }     int ans=0;     for(int j=0;j<26;j++)ans=max(ans,f[n][j]);     cout<<ans; }
   | 
 
感觉区域赛的dp还是非常考验大抽象级别的思维量,非常的灵活
比较有特点的是去年威海站的打砖块,某div2F题red and black
acwing宝藏和昨晚的cf#281 E题