Codeforces 1198 & 1199
1198 D
你需要维护一个序列,支持两种操作:
- 对于 \(1\le i\le n\) , \(a[i] \leftarrow \max(a[i],x)\) ;
- 对于给定的 \(p\) , \(a[p] \leftarrow x\) 。
\((1\le n,Q\le 2*10^5)\)
Examples
input
4
1 2 3 4
3
2 3
1 2 2
2 1
output
3 2 3 4
input
5
3 50 2 1 10
3
1 2 0
2 8
1 3 20
output
8 8 20 8 10
解
线段树。
Code
#include<bits/stdc++.h>
using namespace std;
const int maxn=200003;
struct node{
int val,z;
}t[maxn<<2];
int n,Q;
void pushdown(int p,int l,int r){
if(l==r){t[p].val=max(t[p].val,t[p].z),t[p].z=0;return;}
t[p<<1].z=max(t[p<<1].z,t[p].z);
t[p<<1|1].z=max(t[p<<1|1].z,t[p].z);
t[p].z=0;
}
void build(int p,int l,int r){
if(l==r){
scanf("%d",&t[p].val);
return;
}
int mid=(l+r)>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
}
void change(int p,int l,int r,int pos,int k){
pushdown(p,l,r);
if(l==r){
t[p].val=k;
return;
}
int mid=(l+r)>>1;
if(pos<=mid)change(p<<1,l,mid,pos,k);
else change(p<<1|1,mid+1,r,pos,k);
}
int query(int p,int l,int r,int pos){
pushdown(p,l,r);
if(l==r)return t[p].val;
int mid=(l+r)>>1;
if(pos<=mid)return query(p<<1,l,mid,pos);
else return query(p<<1|1,mid+1,r,pos);
}
int main(){
scanf("%d",&n);
build(1,1,n);
scanf("%d",&Q);
int mo,x,y;
while(Q--){
scanf("%d%d",&mo,&x);
if(mo==1){
scanf("%d",&y);
change(1,1,n,x,y);
}
else{
pushdown(1,1,n);
t[1].z=x;
}
}
for(int i=1;i<=n;i++)printf("%d ",query(1,1,n,i));
return 0;
}
1199 E
给你一个 \(3*n\) 个节点 \(m\) 条边的无向图,如果存在一个匹配大小为 \(n\) 输出这个匹配;否则如果存在一个独立集大小为 \(n\) 输出这个独立集;否则输出Impossible。\((n\le 10^5,m\le 5*10^5)\)
Example
input
4
1 2
1 3
1 2
1 2
1 3
1 2
2 5
1 2
3 1
1 4
5 1
1 6
2 15
1 2
1 3
1 4
1 5
1 6
2 3
2 4
2 5
2 6
3 4
3 5
3 6
4 5
4 6
5 6
output
Matching
2
IndSet
1
IndSet
2 4
Matching
1 15
解
先找出任意一个匹配,如果匹配大小 \(\geq n\) ,输出;否则找出所有不在这个匹配中的节点,这些节点构成的集合一定是原图的一个独立集,输出。不存在Impossible的情况。
证明:如果匹配大小 \(<n\) ,那么剩下的节点数量肯定 \(\geq n\) 。
Code
#include<bits/stdc++.h>
using namespace std;
const int maxn=500003;
bool vis[maxn];
int n,m,ANS[maxn];
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
for(int i=1;i<=3*n;i++)vis[i]=0;
int CNT=0;
for(int i=1;i<=m;i++){
int u,v;
scanf("%d%d",&u,&v);
if(!vis[u]&&!vis[v]){
vis[u]=vis[v]=1;
ANS[++CNT]=i;
}
}
if(CNT>=n){
puts("Matching");
for(int i=1;i<=n;i++)printf("%d%c",ANS[i],i<n?' ':'\n');
}
else{
puts("IndSet");
CNT=0;
for(int i=1;i<=3*n;i++)if(!vis[i])ANS[++CNT]=i;
assert(CNT>=n);
for(int i=1;i<=n;i++)printf("%d%c",ANS[i],i<n?' ':'\n');
}
}
return 0;
}
1199 F
有一个 \(n*n\) 的矩阵,有些位置是黑的,有些是白的,现在你有若干次染色机会,每次你可以选择一个高为 \(h\) 宽为 \(w\) 的长方形,把其中的元素全染成白色,代价为 \(\max(h,w)\) 。求最小代价使得能把整个矩阵染成白色。 \((n\le 50)\)
Examples
input
3
###
#.#
###
output
3
input
3
...
...
...
output
0
input
4
#...
....
....
#...
output
2
input
5
#...#
.#.#.
.....
.#...
#....
output
5
解
\(O(n^5)\;\text{dp}\) 。
Code
#include<bits/stdc++.h>
using namespace std;
const int maxn=53;
int n,sum[maxn][maxn],dp[maxn][maxn][maxn][maxn];
char s[maxn][maxn];
int val(int ii,int jj,int i,int j){
return sum[i][j]-sum[ii-1][j]-sum[i][jj-1]+sum[ii-1][jj-1]?max(i-ii+1,j-jj+1):0;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",s[i]+1);
for(int j=1;j<=n;j++){
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+(s[i][j]=='#');
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int ii=i;ii>=1;ii--){
for(int jj=j;jj>=1;jj--){
dp[ii][jj][i][j]=val(ii,jj,i,j);
for(int k=ii;k<i;k++){
dp[ii][jj][i][j]=min(dp[ii][jj][i][j],dp[ii][jj][k][j]+dp[k+1][jj][i][j]);
}
for(int k=jj;k<j;k++){
dp[ii][jj][i][j]=min(dp[ii][jj][i][j],dp[ii][jj][i][k]+dp[ii][k+1][i][j]);
}
}
}
}
}
printf("%d\n",dp[1][1][n][n]);
return 0;
}
1198 E
字符矩阵形式输入变成两个坐标输入,两个坐标之间的所有格子都是黑的;代价变为 \(\min(h,w)\) ; \(n\le 10^9\) ;其它同1199 F。
Examples
inputCopy
10 2
4 1 5 10
1 4 10 5
outputCopy
4
inputCopy
7 6
2 1 2 1
4 2 4 3
2 5 2 5
2 3 5 3
1 2 1 2
3 2 5 3
outputCopy
3
解
本题做法和上题风马牛不相及。
首先肯定是一行一行或一列一列删最优。
看到算法标签有网络流和二分图匹配,于是想到经典的二分图最小点覆盖。
于是,先把所有区间的左端点值和右端点值+1离散化,设离散化数组为 \(mp[i]\) ,然后建二分图,每个节点 \(i\) 代表区间 \([mp[i],mp[i+1])\) ,然后对于每个输入中的矩形,把对应的节点连边。本题不是普通的二分图,所以推荐使用dinic跑。
Code
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=40003,maxm=400003,INF=1050000000;
struct edge{int to,next,w;}e[maxm<<1];
int head[maxn],head1[maxn],cnte;
void add(int u,int v,int w){e[++cnte].to=v,e[cnte].w=w,e[cnte].next=head[u],head[u]=cnte;}
void addedge(int u,int v,int w){add(u,v,w),add(v,u,0);}
int n,s,t,dep[maxn],q[maxn];
bool bfs(){
for(int i=1;i<=n;i++)dep[i]=0,head1[i]=head[i];
dep[s]=1;
int *qhead=q,*qtail=q;
*qtail++=s;
while(qhead!=qtail){
int u=*qhead++;
for(int i=head[u];~i;i=e[i].next){
int v=e[i].to;
if(e[i].w&&dep[v]==0){
dep[v]=dep[u]+1;
*qtail++=v;
}
}
}
return dep[t]!=0;
}
int dfs(int u,int low){
if(low==0||u==t)return low;
int flow=0;
for(int &i=head1[u];~i;i=e[i].next){
int v=e[i].to;
if(e[i].w&&dep[v]==dep[u]+1){
int tmp=dfs(v,min(low,e[i].w));
if(tmp==0)dep[v]=0;
else{
flow+=tmp;
low-=tmp;
e[i].w-=tmp;
e[i^1].w+=tmp;
if(low==0)break;
}
}
}
return flow;
}
int dinic(){
int ans=0;
while(bfs())ans+=dfs(s,INF);
return ans;
}
int N,M,x1[maxn],x2[maxn],y1[maxn],y2[maxn],mp[maxn],cntmp;
int main(){
scanf("%d%d",&N,&M);
for(int i=1;i<=M;i++){
scanf("%d%d%d%d",x1+i,y1+i,x2+i,y2+i);
x2[i]++,y2[i]++;
mp[++cntmp]=x1[i],mp[++cntmp]=y1[i],mp[++cntmp]=x2[i],mp[++cntmp]=y2[i];
}
sort(mp+1,mp+cntmp+1);
cntmp=unique(mp+1,mp+cntmp+1)-mp-1;
s=cntmp*2+1,n=t=s+1;
for(int i=1;i<=n;i++)head[i]=-1;
cnte=-1;
for(int i=1;i<=M;i++){
x1[i]=lower_bound(mp+1,mp+cntmp+1,x1[i])-mp;
y1[i]=lower_bound(mp+1,mp+cntmp+1,y1[i])-mp;
x2[i]=lower_bound(mp+1,mp+cntmp+1,x2[i])-mp;
y2[i]=lower_bound(mp+1,mp+cntmp+1,y2[i])-mp;
for(int j=x1[i];j<x2[i];j++){
for(int k=y1[i];k<y2[i];k++){
addedge(j,k+cntmp,INF);
}
}
}
for(int i=1;i<cntmp;i++){
addedge(s,i,mp[i+1]-mp[i]);
addedge(i+cntmp,t,mp[i+1]-mp[i]);
}
printf("%d\n",dinic());
return 0;
}
1198 F
有一个数组,现在你要把它分成两部分,要求每部分的\(\gcd\)值为1。不可能输出NO。 \((n\le 10^5)\)
Examples
input
4
2 3 6 7
output
YES
2 2 1 1
input
5
6 15 35 77 22
output
YES
2 1 2 1 1
input
5
6 10 15 1000 75
output
NO
解
看代码,你的嘴巴会张大
Code
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> P;
const int maxn=100003;
int n,b[maxn];
P a[maxn];
int main(){
srand(19260817);
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i].first),a[i].second=i;
while(double(clock())/CLOCKS_PER_SEC<0.47){
random_shuffle(a+1,a+n+1);
int g1=0,g2=0;
for(int i=1;i<=n;i++){
if(rand()&1){
if(!g1)g1=a[i].first,b[a[i].second]=1;
else if(a[i].first%g1)g1=__gcd(g1,a[i].first),b[a[i].second]=1;
else g2=__gcd(g2,a[i].first),b[a[i].second]=2;
}
else{
if(!g2)g2=a[i].first,b[a[i].second]=2;
else if(a[i].first%g2)g2=__gcd(g2,a[i].first),b[a[i].second]=2;
else g1=__gcd(g1,a[i].first),b[a[i].second]=1;
}
}
if(g1==1&&g2==1){
puts("YES");
for(int i=1;i<=n;i++)printf("%d ",b[i]);
return 0;
}
}
puts("NO");
return 0;
}