CF1239B The World Is Just a Programming Task
题目描述
定义一个括号序列s是优秀的,当且仅当它是以下几种情况的一种:
1.|s|=0
2.s=‘(’+t+‘)’,其中t是优秀的
3.s=t1+t2,其中t1、t2都是优秀的
一个括号序列的价值为将它看成一个循环串,从多少个位置切开,能切出循环串。
给出一个长度为(n)的括号序列,你可以交换其中两个位置的括号(这两个位置可以相等),问最大价值及方案。
输出任意一种方案均正确。(nleq 3 imes 10^5)
一行题解
“当前左括号数-右括号数”形成折线=>答案为折线中最低点个数=>发现交换配对的括号最赚=>维护贡献
题解
这个人讲得很清楚。
为了方便考虑,先特判左括号的个数是不是和右括号的个数不相等。
当左右括号相等时,一定存在一个位置切开循环串后得到优秀的括号序列。先随便找一个位置把它切开。
以位置为横坐标(i),以该位置及之前的括号序列中的左括号个数-右括号个数为纵坐标(a_i),建系,得到一条折线,最低点纵坐标为0。横坐标为0和为(n)的点纵坐标都是0。
发现从纵坐标为“所有纵坐标的最小值”的地方切开一定会得到优秀的序列。
考虑交换一对括号的作用。首先,交换相同方向的括号没有任何意义。
如果交换的是前面的右括号和后面的左括号,会使一段折线的坐标+2。因为横坐标为0和为(n)的点一定不会被包含,所以全局纵坐标最小值还是0,使一段折线坐标增加不会使最小值个数增加。
如果交换的是前面的左括号(x)和后面的右括号(y),会使一段折线的坐标-2。如果(y)不是(x)匹配右括号(p_x)的:当(y<p_x)时,([x,y])中最低点在(x)处出现,随着(y)增加,和最低点纵坐标相同的点只会越来越多;当(y>p_x)时,考虑(y=p_x+1)时,因为(y)是右括号,(a_y=a_{p_x}-1),使([x,p_x])中纵坐标等于(x)的点全部报废,这时就要前移(x)。
所以要求出每个括号匹配的括号,再用【随便什么数据结构】维护区间最小值和最小值的个数。
代码
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#define rep(i,x,y) for(register int i=(x);i<=(y);++i)
#define dwn(i,x,y) for(register int i=(x);i>=(y);--i)
#define view(u,k) for(int k=fir[u];~k;k=nxt[k])
#define LL long long
#define maxn 300007
#define ls (u<<1)
#define rs (u<<1|1)
#define mi (l+r>>1)
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
void write(int x)
{
if(x==0){putchar('0'),putchar('
');return;}
int f=0;char ch[20];
if(x<0)putchar('-'),x=-x;
while(x)ch[++f]=x%10+'0',x/=10;
while(f)putchar(ch[f--]);
putchar('
');
return;
}
int mn[maxn<<2],num[maxn<<2];
int n,cnt0,maxx,maxi,maxj,tp,stk[maxn],pos[maxn],a[maxn],bac[maxn];
char s[maxn],t[maxn];
void work()
{
tp=0;int po=0;
rep(i,1,n)
{
if(s[i]=='(')stk[++tp]=i;
else{if(!tp)po=i;else tp--;}
}
if(po)
{
int cut=po+1;
int cntt=0;
rep(i,cut,n)t[++cntt]=s[i],bac[cntt]=i;
rep(i,1,cut-1)t[++cntt]=s[i],bac[cntt]=i;
}
else {rep(i,1,n)t[i]=s[i],bac[i]=i;}tp=0;
rep(i,1,n)
{
if(t[i]=='(')stk[++tp]=i;
else pos[i]=stk[tp],tp--;
}
return;
}
void pu(int u)
{
mn[u]=min(mn[ls],mn[rs]),num[u]=0;
if(mn[u]==mn[ls])num[u]=num[ls];
if(mn[u]==mn[rs])num[u]+=num[rs];
}
void build(int u,int l,int r)
{
if(l==r){mn[u]=a[l],num[u]=1;return;}
build(ls,l,mi),build(rs,mi+1,r),pu(u);
}
void ask(int u,int l,int r,int x,int y,int & ansmn,int & ansnum)
{
if(x<=l&&r<=y)
{
if(mn[u]==ansmn)ansnum+=num[u];
else if(mn[u]<ansmn)ansmn=mn[u],ansnum=num[u];
return;
}
if(x<=mi)ask(ls,l,mi,x,y,ansmn,ansnum);
if(y>mi)ask(rs,mi+1,r,x,y,ansmn,ansnum);
return;
}
int main()
{
scanf("%d%s",&n,s+1);
if(n&1){puts("0"),puts("1 1");return 0;}
rep(i,1,n)if(s[i]=='(')cnt0++;
if(cnt0!=n/2){puts("0"),puts("1 1");return 0;}
work();
rep(i,1,n){a[i]=a[i-1]+(t[i]=='('?1:-1);}
build(1,1,n);int num0=num[1];
maxx=num0,maxi=maxj=1;
rep(i,1,n)if(t[i]==')')
{
int aa=2147483647,bb=0,ansnow=0;ask(1,1,n,pos[i],i-1,aa,bb);
if(aa>2)continue;
if(aa==2)ansnow=num0+bb;
else ansnow=bb;
if(ansnow>maxx)maxx=ansnow,maxi=bac[pos[i]],maxj=bac[i];
}
printf("%d
%d %d",maxx,maxi,maxj);
return 0;
}
CF1239C Queue in the Train
题目描述
火车上有编号为1,2,...,(n)的(n)个人,第(i)个人会在第(t_i)秒醒来,他们醒来后都想接水,每个人接水都要花(p)分钟。
同时只会有一个人能接水,接水过程中不会被打断。其他去接水的人从去接水到接到水的过程中都会排队。
一个人醒来后,只有在发现编号不小于他的人中不存在“正在接水”或“正在排队”或“醒来了且没接到水”的人时,才会去接水。
人走到饮水机不需要时间。
求每个人接完水的时间。(nleq 10^5;q,tleq 10^9)
题解
模拟这个过程。
把所有人以醒来时间为第一关键字,编号为第二关键字排序。
用队列维护正在排队的人。
用以编号为关键字的小根堆维护已经醒来但没接到水的人。
代码
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<map>
#include<queue>
#include<set>
#include<stack>
#include<vector>
#define rep(i,x,y) for(register int i=(x);i<=(y);++i)
#define dwn(i,x,y) for(register int i=(x);i>=(y);--i)
#define view(u,k) for(int k=fir[u];~k;k=nxt[k])
#define LL long long
#define maxn 100007
#define mp make_pair
#define pii pair<LL ,LL>
#define fi first
#define se second
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
return x*f;
}
void write(LL x)
{
if(x==0){putchar('0'),putchar(' ');return;}
int f=0;char ch[20];
if(x<0)putchar('-'),x=-x;
while(x)ch[++f]=x%10+'0',x/=10;
while(f)putchar(ch[f--]);
putchar(' ');
return;
}
int n,tr[maxn];
LL t[maxn];
LL p,ans[maxn],Q[maxn],hd=1,tl;
pii a[maxn];
priority_queue<int >q;
int lt(int x){return x&(-x);}
void add(int x,int k){for(;x<=n;x+=lt(x))tr[x]+=k;return;}
int ask(int x){int k=0;for(;x;x-=lt(x))k+=tr[x];return k;}
int main()
{
n=read(),p=read();
rep(i,1,n)t[i]=read(),a[i].fi=t[i],a[i].se=i;
sort(a+1,a+n+1);
LL start=0,tim=0,now=1;
rep(i,1,n)
{
while(now<=n&&a[now].fi<=tim)
{
if(a[now].se<start&&!ask(a[now].se))add(a[now].se,1),Q[++tl]=now;
else q.push(-a[now].se);now++;
}
if(hd<=tl)add(a[Q[hd]].se,-1),start=a[Q[hd]].se,hd++;
else if(!q.empty())start=-q.top(),q.pop();
else{start=a[now].se,tim=a[now].fi,now++;}
tim+=p,ans[start]=tim;
}
rep(i,1,n)write(ans[i]);
return 0;
}
CF1239D Catowice City
题目描述
有编号为(1,2,...,n)的(n)个人和编号为(1,2,...,n)的(n)只猫。第(i)号人认识第(i)号猫。
有(m)个形如((x,y))的额外的条件,表示第(x)号人认识第(y)号猫。
问能否选出至少一个人、至少一只猫,使所有人不认识所有猫,并且人数+猫数=(n)。如果能,输出方案。
(n,mleq 10^6)
一行题解
“互不认识”+“i号人认识i号猫”=>2-SAT=>只有“人->人”、“猫->猫”之间的边且同一对有边的人和有边的猫的边刚好相反=>只选一个无出边的强连通分量
题解
如果只是选出(n)个动物使选出来的所有人不认识所有猫,那么会发现第(i)号人和第(i)号猫中选且只选一个。
这样就可以2-SAT解决:点(i)表示选(i)号人,点(i')表示选(i)号猫;对于条件((x,y)),需要建边(x->y)、(y'->x),表示选(x)号人后必须选(y)号人,选(y)号猫后必须选(x)号猫。
再考虑条件“至少一个人、至少一只猫”:发现这个图比较特殊,只有人之间的边和猫之间的边,并且人之间的边形成的拓扑图刚好和猫之间的边形成的拓扑图相反。这相当于每个人的强连通分量中的点的编号和对应的猫的强连通分量中点的编号相同。也就是说,可以选一个没有出边的强连通分量(记为S),强行选这个强连通分量的所有点,并且不选同一种动物的其它强连通分量的所有点。对于另一种动物,强行不选S中的所有点,选不在S中的所有点。发现这样构造一定符合2-SAT的性质。
当且仅当存在大小为(n)的强连通分量时,对于每一种动物都只有1个强连通分量,无法按上述方法构造。
代码
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<set>
#include<stack>
#include<vector>
#include<queue>
#define LL long long
#define maxn 2000007
#define rep(i,x,y) for(int i=(x);i<=(y);++i)
#define dwn(i,x,y) for(int i=(x);i>=(y);--i)
#define view(u,k) for(int k=fir[u];~k;k=nxt[k])
using namespace std;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)&&ch!='-')ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(isdigit(ch))x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
void write(int x)
{
int f=0;char ch[20];
if(x==0){putchar('0');putchar('
');return ;}
if(x<0){putchar('-'),x=-x;}
while(x)ch[++f]=x%10+'0',x/=10;
while(f)putchar(ch[f--]);putchar('
');
}
int n,m,no,fir[maxn],nxt[maxn],v[maxn],cnte,dfn[maxn],low[maxn],ans[maxn],ins[maxn],stk[maxn],tp,tim,col[maxn],num;
void ade(int u1,int v1){v[cnte]=v1,nxt[cnte]=fir[u1],fir[u1]=cnte++;}
void tar(int u)
{
dfn[u]=low[u]=++tim,ins[u]=1,stk[++tp]=u;
view(u,k)
{
if(!dfn[v[k]])tar(v[k]),low[u]=min(low[u],low[v[k]]);
else if(ins[v[k]])low[u]=min(low[u],dfn[v[k]]);
}
if(dfn[u]==low[u])
{
num++;int cnt=0;
while(1)
{
cnt++;
col[stk[tp]]=num,ins[stk[tp]]=0;
if(stk[tp--]==u)break;
}
if(cnt==n)no=1;
}
}
int gx(int x,int f){return x+f*n;}
int main()
{
int t=read();
while(t--)
{
n=read(),m=read(),no=0;int li=n<<1;
rep(i,1,li)dfn[i]=0,fir[i]=-1;cnte=tim=num=0;
rep(i,1,m)
{
int x=read(),y=read();
if(x==y)continue;
ade(gx(x,0),gx(y,0)),ade(gx(y,1),gx(x,1));
}
int c[2],co=1;c[0]=c[1]=0;
rep(i,1,li)if(!dfn[i])tar(i);
if(no){puts("No");continue;}
rep(i,1,n)if(col[i]==1)co=0;
rep(i,1,n)
{
if(col[gx(i,0)]==col[gx(i,1)]){puts("No");no=1;break;}
if(col[gx(i,co)]==1)ans[i]=co,c[co]++;
else ans[i]=co^1,c[co^1]++;
}
if(no)continue;
puts("Yes");
printf("%d %d
",c[0],c[1]);
rep(i,1,n)if(!ans[i])printf("%d ",i);puts("");
rep(i,1,n)if(ans[i])printf("%d ",i);puts("");
}
return 0;
}
一些感想
伟大的ysf口胡的!!!
The World Is Just a Shitful Task