2022牛客多校第七场
作者:互联网
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