ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

2022牛客多校第七场

2022-08-13 00:02:13  阅读:141  来源: 互联网

标签:nxt int rep 第七场 多校 牛客 flag now dp


2022牛客多校第七场

过程

本场首先C题签到,一道小构造,随后F想了想直接暴力删除即可,之后G在理解题意后做出,随后便开始坐牢,队友和我在J上的dp为五次方,没敢下手,,随后在最后时刻想明白了K,但已经没时间下手了,惨淡收场。

题解

C

生成一个排列使得\(P_i\not ={}A_i\),首先如果\(A_i\)全相等则无解,否则我们直接默认\(P_i=i\),如果只有一个相同的位置,那只要找到一个可交换的交换即可,否则将多个相同的位置依次交换即可。

int n;
int a[maxn],b[maxn];
bool check(){
    int now=a[1],flag=1;
    rep(i,2,n){
        if(a[i]!=now) flag=0;
    }
    return flag;
}
vector<int>c;
void solve(){
    cin>>n;
    rep(i,1,n) scanf("%d",&a[i]);
    if(check()){puts("NO");return;}
    puts("YES");
    rep(i,1,n) b[i]=i;
    c.clear();
    rep(i,1,n){
        if(i==a[i]) c.pb(i);
    }
    if(c.size()==1){
        int tmp=c[0];
        rep(i,1,n){
            if(a[i]!=tmp&&i!=a[tmp]) {    
                swap(b[i],b[tmp]);break;
            }
        }
    }
    else{
        int sze=c.size();
        rep(i,1,sze-1){
            swap(b[c[i]],b[c[i-1]]);
        }
    }
    rep(i,1,n) printf("%d ",b[i]);
    pts;
}  

F

暴力删除,用双向链表模拟即可,注意判断边界条件和停止条件即可。

int n,x,a[maxn];
int from[maxn],nxt[maxn];
int vis[maxn];
bool check(int now){
    bool flag=(a[now]+a[nxt[now]]==x)||(a[now]==a[nxt[now]]);
    flag&=(!vis[now] && !vis[nxt[now]]);
    flag&=(now!=nxt[now]);
    return flag;
}
void solve(){
    cin>>n>>x;
    rep(i,1,n){
        scanf("%d",&a[i]);
        from[i]=i-1;
        nxt[i]=i+1;
    }
    from[1]=n;nxt[n]=1;
    int now=1,cnt=0;
    int ans=0;
    while(1){
        if(check(now)){
            vis[now]=vis[nxt[now]]=1;
            from[nxt[nxt[now]]]=from[now];
            nxt[from[now]]=nxt[nxt[now]];
            now=from[now];ans++;
        }
        else now=nxt[now];
        if(now==nxt[now]) break;
        cnt++;
        if(cnt>=3*n) break;
    }
    cout<<ans<<endl;
}  

G

题意是表达字符串的最短正则表达式的长度和数量,在理解正则表达式后不难得出结论。

int n;
char s[maxn];
bool check(){
    int now=s[1],flag=1;
    rep(i,2,n) if(now!=s[i]) flag=0;
    return flag;
}
void solve(){
    scanf("%s",s+1);
    n=strlen(s+1);
    bool flag=check();
    if(n==1) printf("1 2\n");
    else if(n==2) {
        if(!flag) printf("2 6\n");
        else printf("2 8\n"); 
    }
    else{
        if(!flag) printf("2 2\n");
        else printf("2 4\n");
    }
}  

J

是一个\(n^5dp\),但因为跑不满,在判断非法状态后跑的飞快。
首先转换题意,因为是区间和可以被k整除,我们可以等价与前缀和模k后相同的前缀和可以构成一个区间使得区间和被k整除,那么这里将区间和转变为区间的两个端点。令\(s_i\)为前缀和模k,\(num_i\)为\(s_x=i\)的个数,则\(\sum_{i=0}^{k-1}num_i=n\),而此时区间和被k整除的个数为\(\sum_{i=0}^{k-1}C(num_i,2)\),我们要求其等于t时的方案数。

此时设dp状态\(dp[i][j][sum]\)表示\(s_i=0..i\)的前缀和的值已被考虑,在\(n\)个位置中占据了\(j\)个位置,被k整除的区间个数为\(sum\),那么最后所求为\(dp[k-1][n][t]\)。考虑如何转移,对于新的\(i+1\),枚举其在n个位置中放多少个,设放\(l\)个则对于状态\(dp[i][j][sum]\),可以有\(C(n-j,l)\)个位置被考虑,因此转移方程\(dp[i+1][j+l][sum+C(l,2)]+=dp[i][j][sum]*C(n-j,l)\)。

初始时化,当在前缀和数组上放0时,单独一个位置也算整除k,故初始化时\(dp[0][j][C(j+1,2)]=C(n,j)\)。随后在dp过程中不合法的条件直接跳过,最后dp复杂度看起来是\(O(n^5)\),但实际上远远跑不满,接近\(O(n^4)\).

int n,k,t;
ll dp[70][70][5000];
struct combinatorial{
 ll inv[maxn],fac[maxn];
 int qpow(ll a,ll b){
  ll c=1;
  while(b){
   if(b&1) c=c*a%mod;
   a=1LL*a*a%mod;b>>=1;
  }
  return c;
 }
 void build(){
  fac[0]=1;
  rep(i,1,N) fac[i]=fac[i-1]*i%mod;
  inv[N]=qpow(fac[N],mod-2)%mod;
  rpe(i,N-1,0) inv[i]=inv[i+1]*(i+1)%mod;
 }
 ll C(int a,int b){
  if(b==0) return 1;
        if(a<b) return 0;
   return fac[a]*inv[b]%mod*inv[a-b]%mod;
 }
}com;
void solve(){
    com.build();
    cin>>n>>k>>t;
    rep(j,0,n) dp[0][j][com.C(j+1,2)]=com.C(n,j);
    rep(i,0,k-1){
        rep(j,0,n){
            rep(v,0,t){
                if(!dp[i][j][v]) continue;
                rep(l,0,n){
                    if(j+l>n) break;
                    if(v+com.C(l,2)>n*n) break;
                    ll &tmp=dp[i+1][j+l][v+com.C(l,2)];
                    tmp=(tmp+dp[i][j][v]*com.C(n-j,l))%mod;
                }
            }
        }
    } 
    cout<<dp[k-1][n][t]<<"\n";
}  

K

博弈+莫队,需要首先分析出博弈的必胜条件。
n堆石子,每堆有\(a_i\)个时:
\(n=1\)时,先手必胜。
\(n=2\)时,若\(a_1\bigoplus a_2=0\),则后手可以模仿先手操作,后手必胜;若\(a_1 \not ={}a_2\),则选手可以通过取来使得\(a_1=a_2\),先手逼胜。
\(n=3\)时,对于三个数\(a_1,a_2,a_3\),不妨令其递增,先手可以从\(a_3\)中拿取\(a1+a3-a2\)个,再将\(a_1,a_3\)合并使得\(a_1=a_2\),这样先手必胜。
\(n=4\)时,因为\(n=3\)先手必胜,所以谁改变堆数谁输,双方均只能拿去,最后终态为全为1,此时先手必输,因此将\(a_i-1\)后,转化为双方取一堆任意石子数量,全取完后无法操作的人输,此时即nim游戏,若\(a_i-1\)的异或和为0,则先手必输,因为后手可以跟着先手做,异或和不为0,则先手可将异或和转为0,后手必输。
\(n=5\)时,那么先手可以通过拿取最大的那一堆并将剩下的与其他堆合并使得n变为4且\(a_i-1\)异或和为0,故此时必胜。
\(n=6\)时同\(n=4\)

因此当n为奇数时必胜,n为偶数且\(a_i-1\)异或和不为0时必胜,为0必输。

那么对于区间询问,因为总的子区间个数为\(\frac{n*(n-1)}{2}\),故求出必输区间减去即可。对于区间内异或和为0的子区间数,可以用莫队来查询,首先处理出\(a_i-1\)的前缀异或和\(S_i\),我们维护两个数组\(s[0][i]\)和\(s[1][i]\)表示区间内偶数位和奇数位上\(i\)出现的次数,每次进入当\(S_i\)进入莫队当前范围时 \(ans+=s[i\%2][S_i],s[i\%2][S_i]++\),删除时相反,即可在\(O(n*\sqrt{n})\) 内解决问题。

int a[maxn],n,m;
struct Que{int l,r,id,bl;}q[maxn];
ll ans[maxn];
int block;
bool cmp(Que x,Que y){
    if(x.bl!=y.bl) return x.l<y.l;
    return x.r<y.r;
}
ll now=0;
int s[2][maxm];
void add(int x){
    now+=s[x&1][a[x]];s[x&1][a[x]]++;
}
void del(int x){
    s[x&1][a[x]]--;now-=s[x&1][a[x]];
}
void Solve(){
    cin>>n>>m;;block=n/sqrt(m);
    rep(i,1,n) scanf("%d",&a[i]);
    rep(i,1,m) {
        int x,y;
        scanf("%d %d",&x,&y);
        q[i]={x-1,y,i,(x-1)/block};
    }
    rep(i,1,n) a[i]=a[i-1]^(a[i]-1);
    sort(q+1,q+1+m,cmp);
    int l=0,r=0;s[0][0]=1;
    rep(i,1,m){
        int L=q[i].l,R=q[i].r;
        while(r<R) add(++r);
        while(r>R) del(r--);
        while(l<L) del(l++);
        while(l>L) add(--l);
        ll tmp=(r-l+1);
        ans[q[i].id]= tmp*(tmp-1)/2 - now; 
    }
    rep(i,1,m) printf("%lld\n",ans[i]);
}  

标签:nxt,int,rep,第七场,多校,牛客,flag,now,dp
来源: https://www.cnblogs.com/Mr-leng/p/16581750.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有