AtCoder Grand Contest 038 刷题记录

AtCoder Grand Contest 038 刷题记录

link to AGC038


A 01 Matrix

大意:请你构造一个$n*m$的01矩阵,再给你$a$和$b$,使得每行的$\min \{ cnt_0,cnt_1 \} = a$,每列的这个东西等于$b$

题解:直接将这个矩阵横竖切两刀,切点是$a$和$b$,左上和右下填同一个数就行了

 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 }
View Code

 

B Sorting a Segment

大意:给你一个排列,以及一个$k$,你可以做一次操作,操作是,选择一个长度为$k$的连续区间,对其排序,问能得到多少种排列

题解:

  • 最多的答案上限是$n-k+1$,我们考虑将重复的减去,两个重复贡献的区间只有以下两种情况
  • 一,两个区间不相交,并且都是升序的
  • 二,两个区间相交,此时左区间滑动到右区间的所有区间都做着同一个重复贡献,于是我们只需判断位置相差$1$的两个区间即可
 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 }
View Code

 

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
$$

 

 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 }
View Code

 

D Unique Path

大意:问你能否构造一个$n$点$m$边的无向联通图(无重边或自环)并满足$q$个条件,条件有两种形式,第一种,$a$和$b$之间的简单路径数唯一,第二种,$a$和$b$之间的简单路径数大于一

题解:

  • 对于所有第一种条件,考虑把它们缩起来,它们必定形成一个森林
  • 若第二种条件连接了同一课树的两个点,就炸了
  • 先把整个图是一棵树的情况判掉,那么图中必定有一个环,每棵树只能存在一个代表节点和其他的树连边,否则会出现一个环使得同一棵树的两个点都在环上
  • 最后判一下$m$是否在边的上下界里即可,下界是已有的边加上树的个数(形成一个环),上界是已有的边加上把森林连成完全图的边数
 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 }
View Code

 

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了
  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 }
View Code

 

posted @ 2019-10-10 13:54  C-S-X  阅读(216)  评论(0编辑  收藏  举报