AtCoder Grand Contest 038 刷题记录
AtCoder Grand Contest 038 刷题记录
A 01 Matrix
大意:请你构造一个$n*m$的01矩阵,再给你$a$和$b$,使得每行的$\min \{ cnt_0,cnt_1 \} = a$,每列的这个东西等于$b$
题解:直接将这个矩阵横竖切两刀,切点是$a$和$b$,左上和右下填同一个数就行了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 #define FOR(i,a,b) for (register int i=(a);i<=(b);i++) 3 #define For(i,a,b) for (register int i=(a);i>=(b);i--) 4 #define mem(i,j) memset(i,j,sizeof(i)) 5 #define GO(u) for (register int j=f[u];j!=-1;j=nxt[j]) 6 using namespace std; 7 typedef long long ll; 8 const int N=1010; 9 int n,m,a,b,ans[N][N]; 10 inline int read() 11 { 12 int x=0,f=1; 13 char c=getchar(); 14 while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} 15 while (c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();} 16 return f*x; 17 } 18 inline void write(int x) 19 { 20 if (x<0) putchar('-'),x=-x; 21 if (x>9) write(x/10); 22 putchar(x%10+'0'); 23 return; 24 } 25 int main() 26 { 27 n=read(),m=read(),a=read(),b=read(); 28 FOR(i,1,n) 29 { 30 FOR(j,1,m) 31 { 32 if (i<=b&&j<=a) ans[i][j]=1; 33 else if (i>b&&j>a) ans[i][j]=1; 34 else ans[i][j]=0; 35 } 36 } 37 FOR(i,1,n) 38 { 39 FOR(j,1,m) printf("%d",ans[i][j]); 40 putchar('\n'); 41 } 42 return 0; 43 }
B Sorting a Segment
大意:给你一个排列,以及一个$k$,你可以做一次操作,操作是,选择一个长度为$k$的连续区间,对其排序,问能得到多少种排列
题解:
- 最多的答案上限是$n-k+1$,我们考虑将重复的减去,两个重复贡献的区间只有以下两种情况
- 一,两个区间不相交,并且都是升序的
- 二,两个区间相交,此时左区间滑动到右区间的所有区间都做着同一个重复贡献,于是我们只需判断位置相差$1$的两个区间即可
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 #define FOR(i,a,b) for (register int i=(a);i<=(b);i++) 3 using namespace std; 4 const int N=2e5+5; 5 int n,k,a[N],ans,st_mn[21][N],st_mx[21][N],b[N],f[N],vis[N]; 6 inline void build_st() 7 { 8 FOR(i,1,n) st_mn[0][i]=st_mx[0][i]=a[i]; 9 FOR(j,1,20) 10 for (register int i=1;i+(1<<j)-1<=n;i++) 11 { 12 st_mn[j][i]=min(st_mn[j-1][i],st_mn[j-1][i+(1<<(j-1))]); 13 st_mx[j][i]=max(st_mx[j-1][i],st_mx[j-1][i+(1<<(j-1))]); 14 } 15 return; 16 } 17 inline int query_min(int l,int r) 18 { 19 int ret,step=log2(r-l+1); 20 ret=min(st_mn[step][l],st_mn[step][r-(1<<step)+1]); 21 return ret; 22 } 23 inline int query_max(int l,int r) 24 { 25 int ret,step=log2(r-l+1); 26 ret=max(st_mx[step][l],st_mx[step][r-(1<<step)+1]); 27 return ret; 28 } 29 inline void pan() 30 { 31 int tag=0; 32 FOR(i,1,n) f[i]=1; 33 FOR(i,2,n) 34 if (a[i]>a[i-1]) f[i]=max(f[i],f[i-1]+1); 35 FOR(i,1,n) if (f[i]>=k&&!vis[i]) ans--,tag=1; 36 if (tag) ans++; 37 return; 38 } 39 int main() 40 { 41 scanf("%d%d",&n,&k); 42 FOR(i,1,n) scanf("%d",&a[i]); 43 build_st(); 44 ans=n-k+1; 45 FOR(i,k+1,n) 46 { 47 int mx=query_max(i-k+1,i-1),mn=query_min(i-k+1,i-1); 48 if (mx<a[i]&&mn>a[i-k]) ans--,vis[i]=1; 49 } 50 pan(); 51 printf("%d\n",ans); 52 return 0; 53 }
C LCMs
大意:给你一个长度为$2e5$,值域为$1e6$的序列$A$,请你求出每对元素的最小公倍数之和
题解:是个简单的反演题,但是一开始中间有个整除条件推错了,使我很傻逼,反正最后的式子就是这个,右边预处理$v_i$表示所有能被$i$整除的$A_j$求和,右边就是$v_T*v_T$,然后就是一个狄利克雷卷积的形式,直接搞就完了
$$
\sum_{T=1}^{mx} \sum_{d|T}\frac{\mu{(\frac{T}{d})}}{d}\sum_{i=1}^n\sum_{j=1}^n[T|A_i][T|A_j]A_iA_j
$$
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 #define FOR(i,a,b) for (register int i=(a);i<=(b);i++) 3 #define For(i,a,b) for (register int i=(a);i>=(b);i--) 4 #define mem(i,j) memset(i,j,sizeof(i)) 5 #define GO(u) for (register int j=f[u];j!=-1;j=nxt[j]) 6 #define fi first 7 #define se second 8 #define pb push_back 9 #define pii pair<int,int> 10 #define MP make_pair 11 using namespace std; 12 typedef long long ll; 13 const int N=2e5+5; 14 const int V=1e6+5; 15 const int mod=998244353; 16 int n,a[N],ans=0,mx=0,sgm=0; 17 int pr[V],vis[V],miu[V],s[V],cnt[V],v[V],inv[V]; 18 inline int read() 19 { 20 int x=0,f=1; 21 char c=getchar(); 22 while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} 23 while (c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();} 24 return f*x; 25 } 26 inline void write(int x) 27 { 28 if (x<0) putchar('-'),x=-x; 29 if (x>9) write(x/10); 30 putchar(x%10+'0'); 31 return; 32 } 33 inline int qpow(int x,int y) 34 { 35 int ret=1; 36 while (y) 37 { 38 if (y&1) ret=1LL*ret*x%mod; 39 y>>=1; 40 x=1LL*x*x%mod; 41 } 42 return ret; 43 } 44 inline void init() 45 { 46 miu[1]=1; 47 FOR(i,2,mx) 48 { 49 if (!vis[i]) pr[++pr[0]]=i,miu[i]=-1; 50 for (register int j=1;j<=pr[0]&&pr[j]*i<=mx;j++) 51 { 52 vis[i*pr[j]]=1; 53 if (i%pr[j]==0) break; 54 miu[i*pr[j]]=-miu[i]; 55 } 56 } 57 FOR(i,1,mx) inv[i]=qpow(i,mod-2); 58 FOR(i,1,mx) miu[i]=(1LL*miu[i]+mod)%mod; 59 FOR(i,1,mx) 60 for (register int j=1;j*i<=mx;j++) 61 s[j*i]=(1LL*s[j*i]+1LL*miu[i]*inv[j]%mod*v[j*i]%mod*v[j*i]%mod)%mod; 62 return; 63 } 64 int main() 65 { 66 // freopen("data.in","r",stdin); 67 n=read(); 68 FOR(i,1,n) a[i]=read(),mx=max(mx,a[i]),cnt[a[i]]++,sgm=(sgm+a[i])%mod; 69 FOR(i,1,mx) 70 for (register int j=1;j*i<=mx;j++) v[i]=(1LL*v[i]+1LL*cnt[j*i]*i%mod*j%mod)%mod; 71 init(); 72 FOR(i,1,mx) ans=(1LL*ans+s[i])%mod; 73 ans=(ans-sgm+mod)%mod; 74 ans=1LL*ans*qpow(2,mod-2)%mod; 75 write(ans);putchar('\n'); 76 return 0; 77 }
D Unique Path
大意:问你能否构造一个$n$点$m$边的无向联通图(无重边或自环)并满足$q$个条件,条件有两种形式,第一种,$a$和$b$之间的简单路径数唯一,第二种,$a$和$b$之间的简单路径数大于一
题解:
- 对于所有第一种条件,考虑把它们缩起来,它们必定形成一个森林
- 若第二种条件连接了同一课树的两个点,就炸了
- 先把整个图是一棵树的情况判掉,那么图中必定有一个环,每棵树只能存在一个代表节点和其他的树连边,否则会出现一个环使得同一棵树的两个点都在环上
- 最后判一下$m$是否在边的上下界里即可,下界是已有的边加上树的个数(形成一个环),上界是已有的边加上把森林连成完全图的边数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 #define FOR(i,a,b) for (register ll i=(a);i<=(b);i++) 3 #define For(i,a,b) for (register ll i=(a);i>=(b);i--) 4 #define mem(i,j) memset(i,j,sizeof(i)) 5 #define GO(u) for (register ll j=f[u];j!=-1;j=nxt[j]) 6 #define fi first 7 #define se second 8 #define pb push_back 9 #define pii pair<ll,ll> 10 #define MP make_pair 11 using namespace std; 12 typedef long long ll; 13 const ll N=1e5+5; 14 ll n,m,q,a[N],b[N],c[N],bian=0,kuai,fa[N]; 15 inline ll read() 16 { 17 ll x=0,f=1; 18 char c=getchar(); 19 while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} 20 while (c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();} 21 return f*x; 22 } 23 inline void write(ll x) 24 { 25 if (x<0) putchar('-'),x=-x; 26 if (x>9) write(x/10); 27 putchar(x%10+'0'); 28 return; 29 } 30 inline ll getfa(ll x) {return (x==fa[x])?x:fa[x]=getfa(fa[x]);} 31 inline void merge(ll x,ll y) 32 { 33 ll fx=getfa(x),fy=getfa(y); 34 if (fx==fy) return; 35 kuai--; 36 bian++; 37 fa[fx]=fy; 38 return; 39 } 40 inline void no() 41 { 42 printf("No\n"); 43 exit(0); 44 } 45 int main() 46 { 47 n=read(),m=read(),q=read(); 48 kuai=n; 49 FOR(i,1,q) a[i]=1+read(),b[i]=1+read(),c[i]=read(); 50 if (m<n) 51 { 52 FOR(i,1,q) if (c[i]) no(); 53 printf("Yes\n"); 54 return 0; 55 } 56 FOR(i,1,n) fa[i]=i; 57 FOR(i,1,q) if (!c[i]) merge(a[i],b[i]); 58 FOR(i,1,q) if (c[i]) 59 { 60 ll fa=getfa(a[i]),fb=getfa(b[i]); 61 if (fa==fb) no(); 62 } 63 if (m<bian+kuai||m>bian+kuai*(kuai-1)/2) no(); 64 printf("Yes\n"); 65 return 0; 66 }
F Two Permutations
大意:给你排列$P$和$Q$,排列$A$和$B$分别是从$P$和$Q$中生成的,即$A_i=i$或者$A_i=P_i$,$B$同理,请最大化$A$和$B$不相同的位置,输出这个数即可
题解:这是二元最小割的套路,借这道题复习一下
- 假如$A$的第$i$位填了$P_i$,那么$A$的第$P_i$位填啥呢,给人一种绕一圈会循环的感觉,于是$i$向$P_i$连边,这会形成一个个环,同一个环上,要么全部取$A_i=i$,要么全部取$A_i=Q_i$,$B$同理处理
- 将源点和汇点看做$A_i=i$和$A_i=Q_i$两种决策,那么每一组$(A_i,B_i)$都存在着二元关系,有四种情况,恰好符合二元关系最小割的建模,于是把这些边连上即可,其中为了整个环同时决策,把每个环看做一个点,再记录一下每个点在哪个环上
- 很容易TLE,把重边加成一条边,不T了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 #define FOR(i,a,b) for (register int i=(a);i<=(b);i++) 3 #define For(i,a,b) for (register int i=(a);i>=(b);i--) 4 #define mem(i,j) memset(i,j,sizeof(i)) 5 #define GO(u) for (register int j=f[u];j!=-1;j=nxt[j]) 6 #define fi first 7 #define se second 8 #define pb push_back 9 #define pii pair<int,int> 10 #define MP make_pair 11 using namespace std; 12 typedef long long ll; 13 const int N=4e5+5; 14 const int M=4e6+5; 15 const int inf=1e8; 16 int n,p[N],q[N],pt=0,ans; 17 int vis[N],x[N],y[N]; 18 int mincut=0,tot=1,f[N],nxt[M],dep[N],S,T,froms[N],toT[N]; 19 queue <int> Q; 20 map<pii,int> mpp; 21 struct E 22 { 23 int u,v,flow; 24 }e[M]; 25 inline int read() 26 { 27 int x=0,f=1; 28 char c=getchar(); 29 while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} 30 while (c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();} 31 return f*x; 32 } 33 inline void write(int x) 34 { 35 if (x<0) putchar('-'),x=-x; 36 if (x>9) write(x/10); 37 putchar(x%10+'0'); 38 return; 39 } 40 inline void add(int u,int v,int flow) 41 { 42 tot++; 43 nxt[tot]=f[u]; 44 f[u]=tot; 45 e[tot]=(E){u,v,flow}; 46 return; 47 } 48 inline void make_edge(int u,int v,int flow) 49 { 50 add(u,v,flow); 51 add(v,u,0); 52 return; 53 } 54 inline void init() 55 { 56 FOR(i,1,n) vis[i]=0; 57 FOR(i,1,n) if (!vis[i]) 58 { 59 int now=i; 60 pt++; 61 while (!vis[now]) vis[now]=1,x[now]=pt,now=p[now]; 62 } 63 FOR(i,1,n) vis[i]=0; 64 FOR(i,1,n) if (!vis[i]) 65 { 66 int now=i; 67 pt++; 68 while (!vis[now]) vis[now]=1,y[now]=pt,now=q[now]; 69 } 70 S=++pt,T=++pt,ans=n; 71 FOR(i,1,n) 72 { 73 if (p[i]==q[i]&&p[i]==i) ans--; 74 else if (p[i]==q[i]&&p[i]!=i) mpp[MP(x[i],y[i])]++,mpp[MP(y[i],x[i])]++; 75 else if (p[i]==i&&p[i]!=q[i]) froms[y[i]]++; 76 else if (q[i]==i&&p[i]!=q[i]) toT[x[i]]++; 77 else if (q[i]!=p[i]&&p[i]!=i&&q[i]!=i) mpp[MP(x[i],y[i])]++; 78 } 79 FOR(i,1,n) 80 { 81 if (p[i]==q[i]&&p[i]==i); 82 else if (p[i]==q[i]&&p[i]!=i) 83 { 84 if (mpp[MP(x[i],y[i])]) make_edge(x[i],y[i],mpp[MP(x[i],y[i])]),mpp[MP(x[i],y[i])]=0; 85 if (mpp[MP(y[i],x[i])]) make_edge(y[i],x[i],mpp[MP(y[i],x[i])]),mpp[MP(y[i],x[i])]=0; 86 } 87 else if (p[i]==i&&p[i]!=q[i]); 88 else if (q[i]==i&&p[i]!=q[i]); 89 else if (q[i]!=p[i]&&p[i]!=i&&q[i]!=i) 90 { 91 if (mpp[MP(x[i],y[i])]) make_edge(x[i],y[i],mpp[MP(x[i],y[i])]),mpp[MP(x[i],y[i])]=0; 92 } 93 } 94 FOR(i,1,pt) 95 { 96 if (froms[i]) make_edge(S,i,froms[i]); 97 if (toT[i]) make_edge(i,T,toT[i]); 98 } 99 return; 100 } 101 inline int bfs() 102 { 103 while (Q.size()) Q.pop(); 104 FOR(i,1,pt) dep[i]=0; 105 dep[S]=1; 106 Q.push(S); 107 while (Q.size()) 108 { 109 int tmp=Q.front(); 110 Q.pop(); 111 GO(tmp) 112 { 113 int v=e[j].v; 114 if (dep[v]) continue; 115 if (!e[j].flow) continue; 116 dep[v]=dep[tmp]+1; 117 if (dep[T]) return 1; 118 Q.push(v); 119 } 120 } 121 return dep[T]; 122 } 123 inline int Dinic(int u,int fl) 124 { 125 int rest=fl,k; 126 if (u==T) return fl; 127 GO(u) 128 { 129 int v=e[j].v; 130 if (dep[v]!=dep[u]+1) continue; 131 if (!e[j].flow) continue; 132 k=Dinic(v,min(rest,e[j].flow)); 133 if (!k) 134 { 135 dep[v]=0; 136 continue; 137 } 138 e[j].flow-=k; 139 e[j^1].flow+=k; 140 rest-=k; 141 } 142 return fl-rest; 143 } 144 int main() 145 { 146 // freopen("data.in","r",stdin); 147 mem(f,-1); 148 n=read(); 149 FOR(i,1,n) p[i]=read()+1; 150 FOR(i,1,n) q[i]=read()+1; 151 init(); 152 while (bfs()) mincut+=Dinic(S,inf); 153 ans-=mincut; 154 write(ans),putchar('\n'); 155 return 0; 156 }