A1. Dances (Easy version)

把 \(a,b\) 序列都从小到大排序,\(a\) 贪心删大的,\(b\) 贪心删小的,二分答案并 \(O(n)\) \(\text{check}\)。

Code
const int N=1e5+5;
int T,n,m;
int a[N],b[N];
il bool check(int mid)
{
for(int i=1,j=mid+1;i<=n-mid;i++,j++) if(a[i]>=b[j]) return 0;
return 1;
}
int main()
{
T=read();
while(T--)
{
n=read(),m=read();
a[1]=1;
for(int i=2;i<=n;i++) a[i]=read();
for(int i=1;i<=n;i++) b[i]=read();
sort(a+1,a+n+1),sort(b+1,b+n+1);
int l=0,r=n;
while(l<r)
{
int mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
printf("%d\n",l);
}
return 0;
}

A2. Dances (Hard Version)

日常给大家提供乐子。

Solution 1

现在的限制变成了 \(m\le 10^9\),肯定不能一个一个求答案。但是发现我们只关心这个 \(a_1\) 和 \(a,b\) 中其余的每个元素的大小关系。也就是说我们只要枚举 \(4\times 10^5\) 种大小关系,对他们分别求解就可以得到所有答案了。

原来对一个给定序列求答案的时间复杂度是 \(O(n\log n)\),考虑优化这个过程。

同样二分删掉的数量 \(mid\),设 \(a_1=x\),\(a\) 序列排序时不包含 \(a_1\)。

那么把 \(x\) 插进 \(a\) 的对应位置,需要判断的区间可以分成 \(3\) 段:\(a\) 序列中在 \(x\) 前的部分,\(x\) 的位置,\(a\) 序列中在 \(x\) 后的部分。

已经有两个 \(\log\) 了,需要 \(O(1)\) 地判断 \([a_l,a_r]\) 与 \([b_L,b_R]\) 对应起来是否合法。

设 \(lst_i\) 表示第一个比 \(b_i\) 小的位置与当前位置的差值。换句话说,取最大的 \(j\) 使 \(a_j<b_i\),令 \(lst_i= j-i\)。那么 \([a_l,a_r]\) 与 \([b_L,b_R]\) 合法的判断条件就是 \(L-l\le \min\{lst_L,\dots,lst_R\}\)。使用 ST 表维护。

时间复杂度 \(O(n \log^2 n)\)。

我也不知道我怎么 5min 想到这个做法调了 1h 的,所以写出来给大家看看乐子。

Solution 2

发现答案只有两种,我们先把 \(a_{2,\dots,n}\) 跟 \(b\) 匹配。

现在加了一个数,要么以上匹配还是合法,要么变不合法了,那就把新加的那个删掉,答案增加 \(1\)。于是就做完了。

Code
#define int long long
const int N=2e5+5;
int T,n,m;
int a[N],b[N],d[N],L[N],tot;
int st[20][N];
il bool check(int l,int r,int L,int R)
{
if(l>r) return 1;
int len=__lg(R-L+1);
int mn=min(st[len][L],st[len][R-(1<<len)+1]);
if(mn<l-L) return 0;
return 1;
}
il bool Check(int mid,int x)
{
int pos=lower_bound(a+1,a+n,x)-a;
if(pos>n-mid&&mid) return check(1,n-mid,mid+1,n);
bool flag1=check(1,pos-1,mid+1,mid+pos-1);
bool flag2=x<b[mid+pos];
bool flag3=check(pos,n-mid-1,mid+pos+1,n);
return flag1&&flag2&&flag3;
}
il int Solve(int x)
{
int l=0,r=n;
while(l<r)
{
int mid=(l+r)>>1;
if(Check(mid,x)) r=mid;
else l=mid+1;
}
return l;
}
void solve()
{
tot=1;
n=read(),m=read(); d[1]=m; d[++tot]=1;
for(int i=1;i<n;i++) a[i]=read(),d[++tot]=a[i];
for(int i=1;i<=n;i++) b[i]=read(),d[++tot]=b[i];
sort(a+1,a+n),sort(b+1,b+n+1),sort(d+1,d+tot+1);
for(int i=1;i<=n;i++)
{
L[i]=lower_bound(a+1,a+n,b[i])-a-1-i;
// cerr<<"L[i] "<<i<<" "<<L[i]<<endl;
st[0][i]=L[i];
}
for(int i=1;i<=__lg(n);i++)
for(int j=1;j<=n-(1<<i)+1;j++) st[i][j]=min(st[i-1][j],st[i-1][j+(1<<i-1)]);
int ans=0;
for(int i=1;i<=tot;i++)
{
if(d[i]!=d[i-1]) ans+=Solve(d[i]);
if(d[i]==m) break;
if(i!=tot&&d[i+1]-d[i]>1) ans+=(d[i+1]-d[i]-1)*Solve(d[i]+1);
}
printf("%lld\n",ans);
}
signed main()
{
int T=read();
while(T--) solve();
}

B. Time Travel

因为时间和时间不好区分,我们不妨令 \(a_i\) 叫时间,\(i\) 叫日期。

设 \(dis_i\) 表示到达点 \(i\) 的最早日期。对每条边记录它所属的时间。

把每个 \(i\) 按 \(a_i\) 分组,就可以二分出在当前 \(dis_u\) 下 \(u\to v\) 这条边可通行的最早日期。

把这个当作边权跑 dijkstra 就行了,双 \(\log\) 但是跑得挺快。

Code
const int N=2e5+5,inf=1e9;
#define pii pair<int,int>
#define fi first
#define se second
int n,t,k,a[N];
map<pii,int> mp;
struct edge{int nxt,to,id;} e[N<<1];
int head[N],cnt=1;
il void add(int u,int v,int id) {e[++cnt]={head[u],v,id};head[u]=cnt;}
vector<int> tim[N],nxt[N];
int dis[N];
il int get(int nowt,int id)
{
auto ans=upper_bound(nxt[id].begin(),nxt[id].end(),nowt);
if(ans==nxt[id].end()) return inf;
else return *ans;
}
int main()
{
n=read(),t=read();
for(int i=1;i<=t;i++)
{
int x=read();
for(int j=1;j<=x;j++)
{
int u=read(),v=read();
add(u,v,i),add(v,u,i);
}
}
k=read();
for(int i=1;i<=k;i++) a[i]=read(),nxt[a[i]].push_back(i);
priority_queue<pii,vector<pii>,greater<pii> >q;
for(int i=1;i<=n;i++) dis[i]=inf;
dis[1]=0,q.push(pii(0,1));
while(!q.empty())
{
int u=q.top().se,f=q.top().fi; q.pop();
if(dis[u]!=f) continue;
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to,id=e[i].id;
int w=get(dis[u],id);
if(dis[v]>w) dis[v]=w,q.push(pii(dis[v],v));
}
}
if(dis[n]==inf) printf("-1\n");
else printf("%d\n",dis[n]);
return 0;
}

C. Minimum Array

被 A2 卡没时间了 /oh

Description

给一个长度为 \(n\) 的初始序列 \(a\) 和 \(q\) 次操作。每次操作形如给 \(a_l\sim a_r\) 的值加上 \(k\)。

依次进行这些操作,求过程中得到过字典序最小的序列。

$n,q\le 5\times 10^5,\ - 10^9\le a_i,k\le 10^9 $。

Solution 1

考虑依次进行每个操作,维护当前能使答案字典序最小的操作编号 \(ans\)。

只考虑从上一个 \(ans\) 到当前时刻的操作,设它们操作后形成的序列为 \(b\)。那么当前的实际序列就是 \([1,ans]\) 的实际序列与 \(b\) 数组对应位相加后的结果。

当前序列比 \([1,ans]\) 字典序更小的充要条件是 \(b\) 中第一个不为 \(0\) 的位置的值 \(<0\),证明是显然的。每次修改后判断,满足这个条件就更新 \(ans\),并清零 \(b\) 数组。

也就是说我们只需要一个数据结构支持区间加、求第一个不为 \(0\) 的位置、单点求值。

直接上线段树是可做的。不过发现第一个不为 \(0\) 的位置只能是某个 \(l_i\) 或 \(r_i+1\),所以把这些点塞进 set 里暴力判断即可,区间加用树状数组维护。

Solution 2

看了下官方题解。

我们直接在差分数组上考虑这个问题,修改变成了单点,更新答案还是求第一个不为 \(0\) 的位置。这样就不用写树状数组了。

Code
#define int long long
const int N=5e5+5,inf=1e9;
int t,n,a[N];
struct node{int l,r,k;} q[N];
struct BIT
{
int tr[N];
il void modify(int x,int k) {for(;x<=n;x+=x&(-x)) tr[x]+=k;}
il void add(int l,int r,int k) {modify(l,k),modify(r+1,-k);}
il int query(int x) {int res=0;for(;x;x-=x&(-x)) res+=tr[x];return res;}
}tr;
int ans;
signed main()
{
int T=read();
while(T--)
{
n=read(); ans=0;
for(int i=1;i<=n;i++) a[i]=read(),tr.tr[i]=0;
t=read(); set<int> st;
for(int i=1;i<=t;i++) q[i].l=read(),q[i].r=read(),q[i].k=read();
for(int i=1;i<=t;i++)
{
tr.add(q[i].l,q[i].r,q[i].k);
st.insert(q[i].l),st.insert(q[i].r+1);
while(st.size()&&!tr.query(*st.begin())) st.erase(st.begin());
if(!st.empty()&&tr.query(*st.begin())<0)
{
for(int j=i;j>ans;j--) tr.add(q[j].l,q[j].r,-q[j].k);
ans=i,st.clear();
}
}
for(int i=1;i<=n;i++) tr.tr[i]=0;
for(int i=1;i<=n;i++) tr.modify(i,a[i]-a[i-1]);
for(int i=1;i<=ans;i++) tr.add(q[i].l,q[i].r,q[i].k);
for(int i=1;i<=n;i++) printf("%lld ",tr.query(i));
printf("\n");
}
}

D. Split

Description

定义一个序列是好的,当且仅当它存在某种划分成左右两部分的方案,使左半部分的最大值严格小于右半部分的最小值。

现给出长度为 \(n\) 的序列 \(a\),保证 \(a\) 是一个 \(1\sim n\) 的排列。

\(q\) 次询问,每次询问 \(a\) 的子区间 \([l,r]\) 是否为好的序列。

\(n,q\le 3\times 10^5\)。

Solution

考虑对于一个确定的序列怎么搞。我们枚举一个值域分界点 \(i\),把 \(\le i\) 的分在一边,\(>i\) 的分在另一边,判断它们是否恰好是原序列的某种划分。

然而这个东西没有单调性,从其它角度优化这个过程。

我们对于每个下标 \(i\),不考虑具体的询问,而是考虑能使 \(a_i\) 成为合法的值域分界点的区间 \([l,r]\) 中 \(l,r\) 的范围。

首先,\(a_i\) 一定在左半部分,且是左半部分的最大值。那么令 \(x\) 为 \(i\) 向左找第一个 \(a_x>a_i\) 的位置。则左端点有限制 \(x< l\le i\)。

右半部分需满足全部值大于 \(a_i\)。那么令 \(y\) 为 \(i\) 右侧第一个 \(a_y>a_i\) 的位置,有限制 \(r\ge y\)。同时令 \(z\) 为 \(y\) 右侧第一个 \(a_z<a_i\) 的位置,有限制 \(y\le r < z\)。

把询问 \([l,r]\) 看作点的坐标,这是一个矩阵加单点查询问题,使用树状数组 + 扫描线维护。

Code
const int N=3e5+5;
int n,m,a[N],pos[N],ans[N],tot;
struct node {int tp,l,r,x,k,y,id;}q[N<<2];
set<int> ls,rs;
il bool cmp(node x,node y)
{
if(x.x!=y.x) return x.x<y.x;
else return x.tp<y.tp;
}
struct BIT
{
int tr[N];
il void modify(int x,int k) {for(;x<=n;x+=x&(-x)) tr[x]+=k;}
il void add(int l,int r,int k) {modify(l,k),modify(r+1,-k);}
il int query(int x) {int res=0;for(;x;x-=x&(-x)) res+=tr[x];return res;}
}tr;
int main()
{
n=read();
for(int i=1;i<=n;i++) a[i]=read(),pos[a[i]]=i,rs.insert(i);
for(int I=1;I<=n;I++)
{
int i=pos[I];
rs.erase(i);
auto itx=rs.lower_bound(i),ity=rs.upper_bound(i);
int x=(itx==rs.begin()||itx==rs.end())?0:*prev(itx);
int y=(ity==rs.end())?n+1:*ity;
auto itz=ls.upper_bound(y);
int z=(itz==ls.end())?n+1:*itz;
q[++tot]={1,x+1,i,y,1,0,0};
q[++tot]={1,x+1,i,z,-1,0,0};
ls.insert(i);
}
m=read();
for(int i=1;i<=m;i++)
{
int l=read(),r=read();
q[++tot]={2,0,0,r,0,l,i};
}
sort(q+1,q+tot+1,cmp);
for(int i=1;i<=tot;i++)
{
if(q[i].tp==1) tr.add(q[i].l,q[i].r,q[i].k);
else ans[q[i].id]=tr.query(q[i].y);
}
for(int i=1;i<=m;i++) printf(ans[i]?"Yes\n":"No\n");
return 0;
}

E. Good Colorings

Description

Alice 和你玩游戏。有一个 \(n\times n\) 的网格,初始时没有颜色。Alice 在游戏开始前依次给其中 \(2n\) 个格子分别涂上了第 \(1\sim 2n\) 种颜色,并告诉你每个颜色的位置。

接下来的每次操作,你可以选择一个未涂色的格子,由 Alice 在 \(2n\) 种颜色中选择一个涂在该格子上,并告诉你该颜色。

如果在某次操作后方格图上存在四个不同颜色的点,且它们的位置形成一个平行于边线的矩形,则输出它们以获得胜利。

你至多进行 \(10\) 次操作,请构造一个获胜方案。交互库自适应,也就是说 Alice 的决策与你的选择有关。

\(T\le200,n\le 1000\)。

Solution

我们把网格图的行列分别看作点,把格子 \((x,y)\) 涂成颜色 \(c\),看作行 \(x\) 向列 \(y\) 连一条边权为 \(c\) 的边。

那么这是一个左右各有 \(n\) 个点的二分图,我们要找的合法矩形就是长度为 \(4\) 且边权互不相同的环。

由于共有 \(2n\) 个点,已经连了 \(2n\) 条不相同的边,则已经连的边一定至少形成一个长度为偶数的环。考虑通过这个环构造答案。

设环上的点分别为 \(a_1,a_2,\dots,a_{2k}\)。每次我们考虑把这个环连一条边对半拆开:

因为整个环上边的颜色都不相同,所以不论新连的边是什么颜色,左右两个环都至少有一个满足环上边的颜色互不相同。选择满足该条件的一侧,重复上述操作,最终一定会得到一个大小为 \(4\) 的环。

由于每次令环长变为原来的一半,至多操作 \(O(\log 2n)-2\) 次。

Code
const int N=2005;
int T,n,f[N][N];
vector<int> e[N],a,b;
stack<int> q;
int in[N],flag,vis[N];
void dfs(int u,int lst)
{
// cerr<<u<<" "<<lst<<" "<<q.size()<<endl;
if(flag) return;
vis[u]=1,in[u]=1,q.push(u);
for(auto v:e[u]) if(v^lst)
{
if(flag) return;
if(in[v])
{
// cerr<<"v "<<v<<" "<<u<<" "<<q.size()<<endl;
assert(q.size());
while(q.top()!=v) a.push_back(q.top()),q.pop();
a.push_back(v),flag=1;return;
}
dfs(v,u);
}
in[u]=0;if(!q.empty()) q.pop();
}
il void clear()
{
// cerr<<"qwq"<<n<<endl;
a.clear(); flag=0;
// cerr<<"?"<<" "<<q.size()<<endl;
while(!q.empty()) q.pop();
// cerr<<"?"<<endl;
for(int i=1;i<=(n<<1);i++)
{
// cerr<<i<<endl;
vis[i]=0,in[i]=0;
for(auto v:e[i]) f[i][v]=0;
e[i].clear();
}
}
il int ask(int x,int y)
{
if(x>y) swap(x,y);
cout<<"? "<<x<<" "<<y-n<<endl;
return read();
}
il void solve()
{
n=read();
for(int i=1;i<=(n<<1);i++)
{
int u=read(),v=read();
e[u].push_back(v+n),e[v+n].push_back(u),f[u][v+n]=f[v+n][u]=i;
}
for(int i=1;i<=n;i++) if(!vis[i]) dfs(i,0);
while(a.size()>4)
{
int sz=a.size()-1,mid=sz/4;
assert(sz&1); int x=a[sz-mid],y=a[mid];
int col=ask(x,y),fg=1;
f[x][y]=f[y][x]=col,e[x].push_back(y),e[y].push_back(x);
for(int i=mid;i<sz-mid;i++) if(f[a[i]][a[i+1]]==col) fg=0;
b.clear();
if(fg) for(int i=mid;i<=sz-mid;i++) b.push_back(a[i]);
else {for(int i=0;i<=mid;i++) b.push_back(a[i]);for(int i=sz-mid;i<=sz;i++) b.push_back(a[i]);}
swap(a,b);
}
assert(a.size()==4);
sort(a.begin(),a.end());
cout<<"! "<<a[0]<<" "<<a[1]<<" "<<a[2]-n<<" "<<a[3]-n<<endl;
string s;cin>>s;
if(s=="ERROR") exit(0);
}
int main()
{
T=read();
while(T--) solve(),clear();
return 0;
}

F. Minimum Segments

不会。

Codeforces Round 905 Div 1 (CF1887)的更多相关文章

  1. Codeforces Round #366 (Div. 2) ABC

    Codeforces Round #366 (Div. 2) A I hate that I love that I hate it水题 #I hate that I love that I hate ...

  2. Codeforces Round #354 (Div. 2) ABCD

    Codeforces Round #354 (Div. 2) Problems     # Name     A Nicholas and Permutation standard input/out ...

  3. Codeforces Round #368 (Div. 2)

    直达–>Codeforces Round #368 (Div. 2) A Brain’s Photos 给你一个NxM的矩阵,一个字母代表一种颜色,如果有”C”,”M”,”Y”三种中任意一种就输 ...

  4. cf之路,1,Codeforces Round #345 (Div. 2)

     cf之路,1,Codeforces Round #345 (Div. 2) ps:昨天第一次参加cf比赛,比赛之前为了熟悉下cf比赛题目的难度.所以做了round#345连试试水的深浅.....   ...

  5. Codeforces Round #279 (Div. 2) ABCDE

    Codeforces Round #279 (Div. 2) 做得我都变绿了! Problems     # Name     A Team Olympiad standard input/outpu ...

  6. Codeforces Round #262 (Div. 2) 1003

    Codeforces Round #262 (Div. 2) 1003 C. Present time limit per test 2 seconds memory limit per test 2 ...

  7. Codeforces Round #262 (Div. 2) 1004

    Codeforces Round #262 (Div. 2) 1004 D. Little Victor and Set time limit per test 1 second memory lim ...

  8. Codeforces Round #371 (Div. 1)

    A: 题目大意: 在一个multiset中要求支持3种操作: 1.增加一个数 2.删去一个数 3.给出一个01序列,问multiset中有多少这样的数,把它的十进制表示中的奇数改成1,偶数改成0后和给 ...

  9. Codeforces Round #268 (Div. 2) ABCD

    CF469 Codeforces Round #268 (Div. 2) http://codeforces.com/contest/469 开学了,时间少,水题就不写题解了,不水的题也不写这么详细了 ...

  10. 贪心+模拟 Codeforces Round #288 (Div. 2) C. Anya and Ghosts

    题目传送门 /* 贪心 + 模拟:首先,如果蜡烛的燃烧时间小于最少需要点燃的蜡烛数一定是-1(蜡烛是1秒点一支), num[g[i]]记录每个鬼访问时已点燃的蜡烛数,若不够,tmp为还需要的蜡烛数, ...

随机推荐

  1. Python 引用问题 - ImportError: attempted relative import with no known parent package

    问题描述 近日在尝试引用其他文件的代码时,遇到了错误: ImportError: attempted relative import with no known parent package. 问题大 ...

  2. H5 WebGL实现水波特效

    前言 零几年刚开始玩电脑的时候,经常在安装程序上看到一种水波特效,鼠标划过去的时候,就像用手在水面划过一样,感觉特别有意思.但是后来,就慢慢很少见过这种特效了.最近突然又想起了这种特效,于是开始折磨怎 ...

  3. 落地微服务架构v2.0

    网关+服务治理 将服务注册到Consul中,需要首先系统该插件. Ocelot:网关组件,腾讯和微软都在使用. 可以使用dotnet run命令启动.NET Core项目,启动时可以在命令时传递监听的 ...

  4. PlayWright(二十)- Pytest之conftest文件

    1.介绍与使用场景 conftest.py 这个是什么呢?   顾名思义,他就是一个文件,那这个文件是干什么用的呢?   在我们上文中,用了fixture函数是直接在用例的文件里定义的,那不能我们所有 ...

  5. C语言基础--数组详细说明

    目录 一.什么是数组 二.一维数组 1.一维数组创建 2.一维数组的使用 2.1 索引值 2.2 遍历数组 2.3 如何使用sizeof()计算出数组的长度 三.二维数组 1.二维数组的创建 2.二维 ...

  6. Spring-配置文件(引入其他配置文件,分模块开发)

    引入其他配置文件 实际开发,Spring的配置文件内容非常多,这就导致了Spring配置很复杂且体积很大,所以可以将配置拆解到其他配置文件中,而在Spring主配置文件通过import标签进行加载 & ...

  7. MyBatis使用注解开发(及Sqlsession连接器的本质)

    使用注解开发 底层实现机制是反射和,动态代码.反射可以获得这个类的方法属性还可以创建对象,执行方法. 面向接口编程 之前学过,面向对象编程,也学习过接口.但是真正的开发中,很多时候我们会选择面向接口编 ...

  8. Anaconda 使用时,conda activate 失败

    今天使用一台电脑上新安装的 anaconda 时,运行 conda activate, 出现如下报错: 错误提示中,说要把 . C:\ProgramData\Anaconda3\etc\profile ...

  9. python将两个列表组合成元组

    point_x = [A_x, B_x, C_x, D_x] point_y = [A_y, B_y, C_y, D_y] points_tulpe = list(zip(point_x, point ...

  10. 论文解读(MCD)《Maximum Classifier Discrepancy for Unsupervised Domain Adaptation》

    Note:[ wechat:Y466551 | 付费咨询,非诚勿扰 ] 论文信息 论文标题:Maximum Classifier Discrepancy for Unsupervised Domain ...