AtCoder Regular Contest 092

时间:2023-03-09 21:42:23
AtCoder Regular Contest 092

AtCoder Regular Contest 092


C - 2D Plane 2N Points

题意:

二维平面上给了\(2N\)个点,其中\(N\)个是\(A\)类点,\(N\)个是\(B\)类点。每个\(A\)类点可以和横纵坐标都比它大的\(B\)类点匹配,求最大匹配数。

分析:

网络流裸题。

#include <bits/stdc++.h>
using namespace std;
#define MAXN 110
#define inf 0x7fffffff
int head[5+MAXN<<1],dep[5+MAXN<<1],num=-1,s,t,n,m,cur[5+MAXN<<1];
struct po
{
int nxt,to,w;
}edge[MAXN*MAXN*2];
struct point
{
int x,y;
}a[MAXN],b[MAXN];
inline void add_edge(int from,int to,int w)
{
edge[++num].nxt=head[from];
edge[num].to=to;
edge[num].w=w;
head[from]=num;
}
inline void add(int from,int to,int w)
{
add_edge(from,to,w);
add_edge(to,from,0);
}
inline bool bfs()
{
memset(dep,0,sizeof(dep));
queue<int>q;
while(!q.empty()) q.pop();
q.push(s); dep[s]=1;
while(!q.empty()){
int u=q.front(); q.pop();
for(int i=head[u];i!=-1;i=edge[i].nxt){
int v=edge[i].to;
if(edge[i].w&&!dep[v]){
dep[v]=dep[u]+1;
if(v==t) return 1;
q.push(v);
}
}
}
return 0;
}
inline int dfs(int u,int low)
{
if(u==t) return low;
int diss=0;
for(int& i=cur[u];i!=-1;i=edge[i].nxt){
int v=edge[i].to;
if(edge[i].w>0&&dep[v]==dep[u]+1){
int check=dfs(v,min(edge[i].w,low));
if(check){
diss+=check;
low-=check;
edge[i].w-=check;
edge[i^1].w+=check;
if(low==0) break;
}
}
}
return diss;
}
inline int dinic()
{
int ans=0;
while(bfs()){
for(int i=s;i<=t;i++) cur[i]=head[i];
while(int d=dfs(s,inf)) ans+=d;
}
return ans;
}
int main()
{
memset(head,-1,sizeof(head));
cin>>n;s=0,t=2*n+1;
for(int i=1;i<=n;i++) cin>>a[i].x>>a[i].y,add(s,i,1);
for(int i=1;i<=n;i++) cin>>b[i].x>>b[i].y,add(i+n,t,1);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) if(a[i].x<b[j].x&&a[i].y<b[j].y) add(i,j+n,1);
cout<<dinic();
}

D - Two Sequences

题意:

给出长度为\(N\)的\(A\)和\(B\)两个数列,求两个数列任意两个数和的异或和。

分析:

可以发现的是,如果两个数\(a,b\)使\(a+b<2^k\)的话,那么他们在\(2^k\)这一位的贡献就是0,如果\(2^{k+1}<a+b<2^{k+1}+2^k\)的话,那么这两个数在\(2^k\)这一位的贡献也为0,对于每一个\(a_i\)二分和在这个范围内的\(b\)。可以得出结果。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN=2e5+7;
ll a[MAXN],b[MAXN],B[MAXN],A[MAXN];
int n;
inline ll find(ll k)
{
int l=1,r=n+1;
while(l<r){
int mid=l+r>>1;
if(B[mid]>=k) r=mid;
else l=mid+1;
}
return l;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
ll ans=0;
for(int k=1;k<=29;k++){
ll base=(1<<k)-1;
for(int i=1;i<=n;i++) B[i]=b[i]&base;
for(int i=1;i<=n;i++) A[i]=a[i]&base;
sort(B+1,B+n+1);
ll cnt=0;
for(int i=1;i<=n;i++)
cnt+=n-find((1<<(k-1))-A[i])+1;
for(int i=1;i<=n;i++)
cnt-=find((1<<k)+(1<<(k-1))-A[i])-find((1<<k)-A[i]);
if(cnt&1) ans+=(1<<(k-1));
}
cout<<ans;
}

E - Both Sides Merger

题意:

有一个数列,每个数的绝对值不会超过\(10^9\)。

有两种操作,一种是删去数列两端的其中一个数,另一种是选择一个不是两端的数,将其替换成它两边数的和,并删去那两个数。

求出最终能得到的最大的数和操作方案。

分析:

可以发现我们要么删去两边的一个数,另一个操作本质上不会改变这个数在数列中的奇偶性质。所以记录一下位置在奇数的正数的和和位置在偶数的位置的和比较一下,然后选择一个更大的,输出方案就可以了。

输出方案方法很多,不详述。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN=1007;
const int inf=0x3fffffff;
int a[MAXN],n,pre[MAXN],cnt,flag,num,maxx=-inf;
ll ans[MAXN],cnt1,cnt2;
inline bool check_all()
{
for(int i=1;i<=n;i++) if(a[i]>0) return 0;
return 1;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
if(check_all()){
int l;
for(int i=1;i<=n;i++) if(a[i]>maxx) maxx=a[i],l=i;
cout<<maxx<<endl<<n-1<<endl;
for(int i=n;i>=l+1;i--) cout<<i<<endl;
for(int i=1;i<=l-1;i++) cout<<1<<endl;
return 0;
}
for(int i=1;i<=n;i++){
if(a[i]>0){
if(i&1) cnt1+=a[i];
else cnt2+=a[i];
}
}
if(cnt1>cnt2){
cout<<cnt1<<endl;
flag=1;
} else {
cout<<cnt2<<endl;
flag=2;
}
for(int i=flag;i<=n;i+=2) if(a[i]>0) pre[++cnt]=i;
int now=pre[cnt];
for(int i=n;i>=pre[cnt]+1;i--) ans[++num]=i;
for(int i=cnt-1;i>=1;i--){
int last=now; now=pre[i];
int mid=last+now>>1;
int k=last-now+1;
while(k>1){
ans[++num]=mid;
mid--;
k-=2;
}
}
for(int i=1;i<=pre[1]-1;i++) ans[++num]=1;
cout<<num<<endl;
for(int i=1;i<=num;i++) cout<<ans[i]<<endl;
}

F - Two Faced Edges

题意:

给出一个有向图,询问翻转每一条边是否会对图中的强连通分量产生影响。

分析:

我们可以对每一条边\(u\)-->\(v\)得出两个观察:

1.如果\(v\)已经可以到达\(u\),那么翻转这条边之后会减少一个强连通分量。

2.如果\(u\)可以不通过这条边而到达\(v\),那么翻转之后就会增加一个强连通分量。

对于第一个我们可以暴力bfs预处理出来。

第二个直接暴力找复杂度不对,思考有没有更优秀的算法。

对于每一个点可以拓展出去的点设这些点的集合为\(S\),对于这些点进行编号,然后从每一个点开始bfs,但是不能经过已经遍历过的点。然后将\(S\)翻转,再次进行bfs。两次bfs编号一样的点就是原点没有第二条路径可以到达的点。

这样就可以在时间复杂度内得出结果。

#include <bits/stdc++.h>
using namespace std;
const int MAXN=1007;
struct nod
{
int to,id;
nod(int a,int b)
{
to=a;
id=b;
}
nod(){}
};
int n,m,tim;
int vis[MAXN],can[MAXN][MAXN][2];
int u[MAXN*MAXN],v[MAXN*MAXN];
vector<nod> edge[MAXN];
void dfs(int now,int fa,int b,int f)
{
vis[now]=tim; can[fa][now][f]=b;
for(int i=0;i<edge[now].size();i++){
int v=edge[now][i].to;
if(vis[v]!=tim) dfs(v,fa,b,f);
}
}
inline void solve(int now)
{
tim++; vis[now]=tim;
for(int i=0;i<edge[now].size();i++){
int v=edge[now][i].to;
if(vis[v]!=tim) dfs(edge[now][i].to,now,edge[now][i].id,0);
}
reverse(edge[now].begin(),edge[now].end());
tim++; vis[now]=tim;
for(int i=0;i<edge[now].size();i++){
if(vis[edge[now][i].to]!=tim) dfs(edge[now][i].to,now,edge[now][i].id,1);
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>u[i]>>v[i];
edge[u[i]].push_back(nod(v[i],i));
}
for(int i=1;i<=n;i++) solve(i);
for(int i=1;i<=m;i++){
if((can[v[i]][u[i]][0]!=0)^0^(can[u[i]][v[i]][0]!=i||can[u[i]][v[i]][1]!=i))
cout<<("diff\n");
else cout<<("same\n");
}
}