====== 2020牛客暑期多校训练营(第六场) ====== ===== 比赛情况 ===== ^题号 ^A ^B ^C ^D ^E ^F ^G ^H ^I ^J ^K | ^状态 |- |O |O |- |O |- |Ø |O |- |O |O | //O 在比赛中通过 Ø 赛后通过 ! 尝试了但是失败了 - 没有尝试// **比赛时间** 2020-07-27 12:00-17:00 ===== 题解 ===== ==== B - Binary Vector ==== 求 $n$ 个 $n$ 维 $01$ 向量线性无关的概率。 找到分子的通项 $2^{\frac {n(n-1)}2} \prod_{i=1}^n(2^i-1)$,线性的 $dp$ 一下求出来。 #pragma GCC optimize(2) #pragma GCC optimize(3,"Ofast","inline") #include #define ALL(x) (x).begin(),(x).end() #define ll long long #define db double #define ull unsigned long long #define pii_ pair #define mp_ make_pair #define pb push_back #define fi first #define se second #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define per(i,a,b) for(int i=(a);i>=(b);i--) #define show1(a) cout<<#a<<" = "< #define ALL(x) (x).begin(),(x).end() #define ll long long #define db double #define ull unsigned long long #define pii_ pair #define mp_ make_pair #define pb push_back #define fi first #define se second #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define per(i,a,b) for(int i=(a);i>=(b);i--) #define show1(a) cout<<#a<<" = "< \\ ==== G - Grid Coloring ==== $n\times n$ 方格框架涂色, $k$ 种颜色出现次数相等,构造一种方案使得每一行每一列都有至少两种颜色,且同色的边不构成环。 从上到下从左到右按 $1$ 到 $k$ 循环填就好了。 {{:2020-2021:teams:wangzai_milk:2020nowcoder6-1.png?400|}} #include #define ll long long #define pii pair #define mp make_pair using namespace std; const int N=205; int r[N][N],c[N][N]; int main() { int t; scanf("%d",&t); while(t--) { int n,k; scanf("%d%d",&n,&k); if(k==1||n==1||2*(n+1)*n%k){printf("-1\n");continue;} int res=0; for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++)r[i][j]=res,res=(res+1)%k; for(int j=1;j<=n+1;j++)c[j][i]=res,res=(res+1)%k; } for(int i=1;i<=n;i++)r[n+1][i]=res,res=(res+1)%k; for(int i=1;i<=n+1;i++) { for(int j=1;j<=n;j++)printf("%d ",c[i][j]+1); puts(""); } for(int i=1;i<=n+1;i++) { for(int j=1;j<=n;j++)printf("%d ",r[i][j]+1); puts(""); } } return 0; } \\ ==== H - Harmony Pairs ==== 数位dp。状态记录一下数位和差值、 $A\le B$ 的限制和 $B\le N$ 的限制即可。 #pragma GCC optimize(2) #pragma GCC optimize(3,"Ofast","inline") #include #define ll long long using namespace std; const int N=105; const ll mod=1e9+7; char s[N]; ll dp[N][2005][2][2]; ll dfs(int dep,int d,bool lim1,bool lim2) { if(dep>=strlen(s))return (d>0); if(dp[dep][d+900][lim1][lim2]!=-1) return dp[dep][d+900][lim1][lim2]; dp[dep][d+900][lim1][lim2] = 0; for(int i=0;i<10;i++) for(int j=0;j<10;j++) { if(lim2&&j>s[dep]-'0')continue; if(lim1&&i>j)continue; dp[dep][d+900][lim1][lim2]+=dfs(dep+1,d+i-j,lim1&&(i==j),lim2&&(j==s[dep]-'0')); dp[dep][d+900][lim1][lim2]%=mod; } return dp[dep][d+900][lim1][lim2]; } int main() { memset(dp,-1,sizeof(dp)); scanf("%s",s); printf("%lld\n",dfs(0,0,1,1)); return 0; } \\ ==== J - Josephus Transform ==== 对排列 $1,2,3,\cdots,n$ 做 $m$ 次操作。每次操作是进行 $x$ 次 $k$ 约瑟夫问题。求最后的排列。 把操作看成置换,如果已知一次 $k$ 约瑟夫问题的置换,那么可以用类似快速幂的方法在 $nmlogx$ 得到答案。考虑如何求一次 $k$ 约瑟夫问题的置换,用线段树暴力模拟就行,复杂度为 $nmlogn$。所以总复杂度$nm(logn + logx)$ #include #define ALL(x) (x).begin(),(x).end() #define ll long long #define db double #define ull unsigned long long #define pii_ pair #define mp_ make_pair #define pb push_back #define fi first #define se second #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define per(i,a,b) for(int i=(a);i>=(b);i--) #define show1(a) cout<<#a<<" = "< \\ ==== K - K-Bag ==== 问给出的序列是否是若干个$1-k$序列组成序列的子序列(好绕) 首先特判序列里有大于k的情况。 其余的上权值线段树,对于前k个位置,只要没有次数大于1就行,后面的dp转移,权值线段树查长度为k的区间是否次数都为1,如果是就转移,然后最后k个位置和前k个位置类似处理。 #include #define il inline using namespace std; typedef long long ll; const int N=5e5+5; const int inf = 1e9+5; int mx[N<<2],a[N],id[N],cnt; bool f[N]; void build(int p,int l,int r) { mx[p] = 0; if (l==r) return ; int mid = (l+r)>>1; build(p<<1,l,mid); build(p<<1|1,mid+1,r); } void Update(int p,int l,int r,int a,int b) { if (l==r) { mx[p]+=b; return ; } int mid = (l+r)>>1; if (a<=mid)Update(p<<1,l,mid,a,b); else Update(p<<1|1,mid+1,r,a,b); mx[p] = max(mx[p<<1],mx[p<<1|1]); } int Getans(int p,int l,int r,int a,int b) { if (l>=a&&r<=b) return mx[p]; int mid = (l+r)>>1; int ans = 0; if (a<=mid)ans = max(ans,Getans(p<<1,l,mid,a,b)); if (b >mid)ans = max(ans,Getans(p<<1|1,mid+1,r,a,b)); return ans; } int getid(int x) { int l = 1,r = cnt+1; while (l>1; if (id[mid] k || a[i] <= 0) flag = true; id[i] = a[i]; } if (flag) {printf("NO\n");continue;} cnt = 0; id[0] = -1; sort(id+1,id+n+1); for (int i = 1;i<= n;i++) if (id[cnt]!=id[i]) id[++cnt] = id[i]; build(1,1,cnt); for (int i = 1;i<= n;i++)f[i] = false; f[0] = true; for (int i = 1;i<= n && i <= k;i++) { Update(1,1,cnt,getid(a[i]),1); if (Getans(1,1,cnt,1,cnt)==1) f[i] = true; } for (int i = k+1;i<= n;i++) { int tp = i-k; Update(1,1,cnt,getid(a[i]),1); Update(1,1,cnt,getid(a[tp]),-1); if (Getans(1,1,cnt,1,cnt)==1) f[i] |= f[i-k]; } int bg = max(1,n-k+1); bool ans = f[n]; for (int i = bg;i<= n && !ans;i++) { if (f[i-1] && Getans(1,1,cnt,1,cnt)==1) ans = true; Update(1,1,cnt,getid(a[i]),-1); } if (ans) printf("YES\n"); else printf("NO\n"); } return 0; } \\