some problem
CF1257F Make Them Similar
$solution:$
折半搜索后考虑如何维护两个数组的和,可以将 $A$ 中每个数减 $A_1$ ,$B$ 中每个数被减 $B_1$ ,$map$ 维护一下即可。
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<map> #include<vector> using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=101; int lowbit(int x){return x&-x;} int cont(int x){int c=0;while(x) c++,x-=lowbit(x);return c;} int N,A[MAXN]; vector<int> sta; map<vector<int> ,int> M; int main(){ N=read();for(int i=1;i<=N;i++) A[i]=read(); for(int i=0;i<(1<<15)-1;i++){ int cur=cont(i^((A[1]>>15)));sta.clear(); for(int j=1;j<=N;j++) sta.push_back(cont(i^(A[j]>>15))-cur); M[sta]=i; } int all=((1<<15)-1); for(int i=0;i<(1<<15)-1;i++){ int cur=cont(i^((A[1]&all)));sta.clear(); for(int j=1;j<=N;j++) sta.push_back(cur-cont(i^(A[j]&all))); if(M.count(sta)){ printf("%d\n",(M[sta]<<15)+i); return 0; } }printf("-1\n");return 0; }
CF1257E The Contest
设 $f_{i,j}$ 表示现在在 $i$ 个数,现在在第 $j$ 段。
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2000001; int col[MAXN],f[MAXN][3],k1,k2,k3,N; int main(){ k1=read(),k2=read(),k3=read();N=k1+k2+k3; for(int i=1;i<=k1;i++) read(); for(int i=1;i<=k2;i++) col[read()]=1; for(int i=1;i<=k3;i++) col[read()]=2; memset(f,127/3,sizeof(f)); f[0][0]=0; for(int i=1;i<=N;i++){ for(int j=0;j<3;j++){ for(int k=0;k<=j;k++) f[i][j]=min(f[i][j],f[i-1][k]+(col[i]!=j)); } } int Minn=min(f[N][0],min(f[N][1],f[N][2])); printf("%d\n",Minn);return 0; }
CF1268C And Reachability
$solution:$
设 $f_{i,j}$ 表示从 $i$ 点走到离 $i$ 最近并且 $j$ 位上是一的,可以简单递推得到。
时间复杂度 $O(20^2\cdot n)$ 。
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=300011; int f[MAXN][21],g[21],n,q,A[MAXN]; int main(){ n=read(),q=read(); for(int i=1;i<=n;i++) A[i]=read(); for(int i=0;i<=n+1;i++) for(int j=0;j<=20;j++) f[i][j]=g[j]=n+1; for(int i=n;i>=1;i--){ for(int j=0;j<=20;j++){ if(A[i]&(1<<j)){ for(int k=0;k<=20;k++) f[i][k]=min(f[i][k],f[g[j]][k]); f[i][j]=i;g[j]=i; } } } while(q--){ int x=read(),y=read();bool ok=0; for(int i=0;i<=20;i++) if(A[y]&(1<<i)) ok|=(f[x][i]<=y); if(ok){printf("Shi\n");continue;} printf("Fou\n"); }return 0; }/* 5 5 0 0 1 3 7 1 2 2 2 3 5 4 5 1 5 */
CF1268B Good Triple
$solution:$
因为 $2k\leq 9$ ,所以直接 $r$ 单调性暴力处理就行。时间复杂度 $O(9n)$ 。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define int long long using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=410001; int L[MAXN],N,A[MAXN]; char str[MAXN]; signed main(){ scanf("%s",str+1);N=strlen(str+1); for(int i=1;i<=N;i++) A[i]=str[i]-'0'; int ll=0,Ans=0; for(int r=1;r<=N;r++){ for(int i=1;r-2*i>=1;i++){ if(A[r]==A[r-i]&&A[r-i]==A[r-2*i]){ll=max(ll,r-2*i);break;} } Ans+=ll; }printf("%lld\n",Ans); }
CSGRound2 逐梦者的初心
$solution:$
考虑将 $S$ 串翻转后1问题就变为了维护多项式系数。$bitset$ 优化即可。
时间复杂度 $O(\dfrac{m^2}{w})$
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<vector> #include<bitset> using namespace std; inline int read(){ int x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } const int N=1000001; const int MAXN=70001; int n,m,A[N],Ans[MAXN]; bitset<MAXN> Bit,ve[1001],e; int main(){ n=read(),m=read(); for(int i=1;i<=m;i++) A[i]=read();for(int i=1;i<=n-m;i++) read(); for(int i=1;i<=m;i++) ve[A[i]][m-i+1]=1; n=m; for(int i=1;i<=m;i++){ int opt=read(),x=read(); if(opt==0) Bit|=(ve[x]<<i); else{Bit=(Bit<<1);Bit|=(ve[x]<<1);} e[m+i]=1;printf("%d\n",(Bit.flip()&e).count()); Bit.flip(); } return 0; }/* 2 2 1 2 1 1 1 3 */
CSGRound2 开拓者的卓识
$solution:$
考虑每一位 $a_i$ 对答案的贡献,根据插板原理可以得到。
$$S_{k,1,r}=\sum_{i=1}^r A_i\cdot \dbinom{i+k-2}{k-1}\cdot \dbinom{r-i+k-1}{k-1}\\=A_i\cdot\dfrac{(i+k-2)!}{(k-1)!\cdot(i-1)!}\cdot\dfrac{(r-i+k-1)!}{(k-1)!\cdot(r-i)!}\\=A_i\cdot\dfrac{(i+k-2)!\cdot (r-i+k-1)!}{(i-1)!\cdot(r-i)!}\cdot\dfrac{1}{(k-1)!^2}$$
$NTT$ 优化一下即可,而阶乘的预处理可以分段打表。
时间复杂度 $O(n\log n+n\times blo)$
CF1225E Rock Is Push
$solution:$
设 $f_{i,j,0/1}$ 表示从 $(1,1)$ 走到 $(i,j)$,其中最后一步的走向是右还是下。
则 $f_{i,j,0}=\sum_{e=k}^{j-1} f_{i,e,1}$ ,可以记录前缀和得到一段区间的 $dp$ 和。
而 $k$ 的得到可以因为实现不同,故时间复杂度为 $O(nm\log n)$ 或 $O(nm)$ 。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define int long long #define mod 1000000007 using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=2011; int n,m,f[MAXN][MAXN][2]; /*left 0 down 1*/ char str[MAXN]; int A[MAXN][MAXN],S1[MAXN][MAXN],S2[MAXN][MAXN]; int Qi(int id,int l,int r){/*col*/ return S1[r][id]-S1[l-1][id]; } int Qj(int id,int l,int r){/*row*/ return S2[id][r]-S2[id][l-1]; } int fS1[MAXN][MAXN],fS2[MAXN][MAXN]; int Mod(int x){return ((x%mod)+mod)%mod;} signed main(){ // freopen("maker.in","r",stdin); n=read(),m=read(); for(int i=1;i<=n;i++){ scanf("%s",str+1); for(int j=1;j<=m;j++) A[i][j]=(str[j]=='R'); } if(n==1&&m==1){printf("%lld\n",A[1][1]^1);return 0;} for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) S1[i][j]=S1[i-1][j]+A[i][j]; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) S2[i][j]=S2[i][j-1]+A[i][j]; f[1][1][0]=f[1][1][1]=1;fS1[1][1]=1,fS2[1][1]=1; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(i==1&&j==1) continue; int l=1,r=i-1,res=i; while(l<=r){ int mid=l+r>>1; if(Qi(j,mid+1,n)<=(n-i)) res=mid,r=mid-1; else l=mid+1; } f[i][j][1]=fS1[i-1][j]-fS1[res-1][j];f[i][j][1]=Mod(f[i][j][1]); l=1,r=j-1,res=j; while(l<=r){ int mid=l+r>>1; if(Qj(i,mid+1,m)<=(m-j)) res=mid,r=mid-1; else l=mid+1; } f[i][j][0]=fS2[i][j-1]-fS2[i][res-1];f[i][j][0]=Mod(f[i][j][0]); fS1[i][j]=fS1[i-1][j]+f[i][j][0];fS1[i][j]=Mod(fS1[i][j]); fS2[i][j]=fS2[i][j-1]+f[i][j][1];fS2[i][j]=Mod(fS2[i][j]); } }printf("%lld\n",(f[n][m][0]+f[n][m][1])%mod);return 0; }/*3 3 .RR ... R.. */
CF1239D Catowice City
$solution:$
考虑类似于 $2-SAT$ 将必须选择的关系连边,$tarjan$ 后让最开始遍历的强连通分量为 $1$ ,因为 $dfs$ 的顺序易知这是正确的。
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #include<vector> using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=1000001; int n,m,T,col[MAXN],sta[MAXN],num,dfn[MAXN],low[MAXN],Ans0[MAXN],Ans1[MAXN]; vector<int> vec[MAXN]; void Init(){ num=0;Ans0[0]=0,Ans1[0]=0; for(int i=0;i<=n;i++) vec[i].clear(); for(int i=0;i<=n;i++) col[i]=dfn[i]=low[i]=0; } void tarjan(int u){ dfn[u]=low[u]=++num;sta[++sta[0]]=u; int siz=vec[u].size(); for(int i=0;i<siz;i++){ int v=vec[u][i]; if(!dfn[v]){ tarjan(v); low[u]=min(low[u],low[v]); }else if(!col[v]) low[u]=min(low[u],low[v]); } if(dfn[u]==low[u]){ col[u]=++col[0]; while(sta[sta[0]]!=u){ col[sta[sta[0]]]=col[0]; sta[0]--; }sta[0]--; }return; } int main(){ // freopen("8.in","r",stdin); T=read(); while(T--){ n=read(),m=read(); Init(); for(int i=1;i<=m;i++){int u=read(),v=read();vec[u].push_back(v);} for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); if(col[0]==1){printf("No\n");continue;} for(int i=1;i<=n;i++){ if(col[i]==1) Ans0[++Ans0[0]]=i; else Ans1[++Ans1[0]]=i; } printf("Yes\n%d %d\n",Ans0[0],Ans1[0]); for(int i=1;i<=Ans0[0];i++) printf("%d ",Ans0[i]);printf("\n"); for(int i=1;i<=Ans1[0];i++) printf("%d ",Ans1[i]);printf("\n"); }return 0; }
CF1244F Chips
$solution:$
考虑若对于 $i$ 来说在 $i-1,i+1$ 有颜色相同的话,那么无论经过多少次操作都可以为本身颜色。
否则,与其相邻的是 $01$ 段,模拟一下即可。
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=4000001; char str[MAXN]; int n,k,A[MAXN],B[MAXN],C[MAXN]; void print(int opt){opt?printf("W"):printf("B");} int main(){ n=read(),k=read(); scanf("%s",str+1); for(int i=1;i<=n;i++) A[i]=(str[i]=='W'); A[0]=A[n],A[n+1]=A[1]; for(int i=1;i<=n;i++) B[i]=(A[i-1]==A[i]||A[i]==A[i+1]),B[i+n]=B[i]; bool F=0;for(int i=1;i<=n;i++) F|=B[i]; if(!F){for(int i=1;i<=n;i++) print(A[i]^(k&1));printf("\n");return 0;} int ps=1;for(int i=1;i<=n;i++) if(B[i]) ps=i; for(int i=n+1;i<=2*n;i++){if(B[i]) ps=i;C[i-n]=i-ps;} for(int i=2*n;i>n;i--) if(B[i]) ps=i; for(int i=n;i>=1;i--){if(B[i]) ps=i;C[i]=min(C[i],ps-i);} for(int i=1;i<=n;i++){ if(B[i]) print(A[i]); else print(A[i]^(min(k,C[i])&1)); }printf("\n");return 0; }
CF1245F Daniel and Spring Cleaning
$solution:$
按维容斥后做简单数位 $dp$ 即可。
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> #define int long long using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int MAXN=30; int T,a,b,A[MAXN],B[MAXN],len,f[MAXN][2][2]; int dfs(int ps,int lim1,int lim2){ if(ps==-1) return 1; if(f[ps][lim1][lim2]!=-1) return f[ps][lim1][lim2]; int e1=(lim1==1)?A[ps]:1,e2=(lim2==1)?B[ps]:1,res=0; for(int i=0;i<=e1;i++) for(int j=0;j<=e2;j++){ if(i==1&&j==1) continue; res+=dfs(ps-1,lim1&&(i==A[ps]),lim2&&(j==B[ps])); } return f[ps][lim1][lim2]=res; } int calc(int x,int y){ if(x<0||y<0) return 0; memset(f,-1,sizeof(f)); len=29; for(int i=29;i>=0;i--) A[i]=(x&(1<<i))?1:0; for(int i=29;i>=0;i--) B[i]=(y&(1<<i))?1:0; return dfs(29,1,1); } signed main(){ // freopen("4.in","r",stdin); T=read(); while(T--){ int l=read(),r=read(); printf("%lld\n",calc(r,r)+calc(l-1,l-1)-2*calc(l-1,r)); } return 0; }
CF1245E Hyakugoku and Ladders
设 $f_{i,j}$ 表示从 $(i,j)$ 走到 $(1,1)$ 的期望步数,按题意模拟的转移即可。
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int N=11; int M[N][N],fro[N*N]; double f[N*N]; int main(){ for(int i=1;i<=10;i++) for(int j=1;j<=10;j++){ if(i&1) M[i][j]=(i-1)*10+j; else M[i][j]=(i-1)*10+11-j; } double sum=0; f[1]=0;for(int i=2;i<=6;i++){ f[i]=(sum+6)/(i-1); sum+=f[i]; } for(int i=1;i<=10;i++){ for(int j=1;j<=10;j++){int x=read();fro[M[i][j]]=M[i-x][j];} } for(int i=7;i<=100;i++){ double s=0; for(int j=1;j<=6;j++) s+=min(f[i-j],f[fro[i-j]]); f[i]=s/6.0+1; } printf("%.10lf\n",f[100]);return 0; }
CF1249F Maximum Weight Subset
$solution:$
设 $f_{i,j}$ 表示以 $i$ 号节点为根的子树下距离 $i$ 最近的点至少为 $j$ 的方案数,枚举哪个要选即可。
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; inline int read(){ int f=1,ans=0;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return f*ans; } const int N=211; int n,k,f[N][N],head[N],cnt,val[N],son[N]; struct node{ int u,v,nex; }x[N<<1]; void add(int u,int v){ x[cnt].u=u,x[cnt].v=v,x[cnt].nex=head[u],head[u]=cnt++; } void dfs(int u,int fath){ for(int i=head[u];i!=-1;i=x[i].nex){ if(x[i].v==fath) continue; dfs(x[i].v,u); } son[0]=0; for(int i=head[u];i!=-1;i=x[i].nex){ if(x[i].v==fath) continue; son[++son[0]]=x[i].v; } for(int i=0;i<=k;i++){ if(i==0){ f[u][0]=val[u]; for(int j=1;j<=son[0];j++) f[u][0]+=f[son[j]][k]; continue; } for(int j=1;j<=son[0];j++){ int res=f[son[j]][i-1]; for(int p=1;p<=son[0];p++){ if(j==p) continue; res+=f[son[p]][max(k-i,i-1)]; } f[u][i]=max(f[u][i],res); } }for(int i=k;i>=0;i--) f[u][i]=max(f[u][i],f[u][i+1]); } int main(){ memset(head,-1,sizeof(head)); n=read(),k=read(); for(int i=1;i<=n;i++) val[i]=read(); for(int i=1;i<n;i++){ int u=read(),v=read(); add(u,v),add(v,u); }dfs(1,0); printf("%d\n",f[1][0]);return 0; }