省选前一周
无
还没很系统的复习,模拟赛一堆思维题和抽象 dp,崩崩崩,急急急,寄寄寄。
WD 和地图
有向图,单点加,强连通分量第 \(k\) 大,删一条边,\(n \le 100000,\ m,q \le 200000\)
图论专题里的题,拿来推。有一个神秘 trick,和相当合适的码量。
最容易想到的是倒序操作,将删边转化为加边。
考虑如果是无向图是 \(water\) 的,那么考虑能否将有向图的限制去掉。
一条有向边,在它被纳入一个强连通分量之前都是无用的,我们可以忽视它,所以我们唯一关心的就是在什么时候它被纳入一个强连通分量。
注意到这个时刻是具有单调性的。考虑二分。
暴力就有一个 \(O(m(n+m)\log q)\) 的做法可以求出每条边被纳入的时刻,即二分时间,每次缩点判断。
考虑优化,每次都缩点是没有用的,考虑一起对所有边进行二分,也就是整体二分。
二分时间,在时间区间 \([L,R]\) 中,对于时间 \(mid\),将出现时间不超过 \(mid\) 的边加入图中,进行缩点。
把边分成两部分,一部分是被纳入强连通分量的,另一部分是剩下的边,前者被纳入的时刻一定小于等于 \(mid\),后者相反,于是每次都能将边分成两部分递归求解。
每次都缩点的复杂度看起来还是 \(O(nm)\) 的,但是注意到每次关心的点只和加入的边有关,假如当前加入了 \(m\) 条边,那么实际有用的点只有 \(2m\) 个。
所以复杂度是 \(O(m\log n \log q)\)。
具体实现,可以用并查集维护强连通分量,向右传的时候直接传缩完的点,然后撤销再向左传,清空时细节处理。
最后线段树合并板子。
code
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define fi first
#define se second
const int N = 1e6+5,inf = 3.6e6;
random_device seed;
mt19937 rd(seed());
int n,m,Q;
int a[N],tot;
pair<int,int> st[N];
bool mp[100][100];
bool vs[N];
int main()
{
freopen("in.in","w",stdout);
n=rd()%5+5; m=rd()%(n*(n-1)>>1)+1; Q=rd()%10+1;
printf("%d %d %d\n",n,m,Q);
for(int i=1;i<=n;i++) printf("%d ",rd()%10+1); putchar('\n');
for(int i=1;i<=m;i++)
{
int x=rd()%n+1,y=rd()%n+1;
while(x==y||mp[x][y]) y=rd()%n+1,x=rd()%n+1; mp[x][y]=1;
if(x!=y) printf("%d %d\n",x,y),st[++tot]={x,y};
}
int cnt=0;
for(int i=1;i<=Q;i++)
{
int op=rd()%3+1;
while(op==1&&cnt==m) op=rd()%3+1;
printf("%d ",op);
if(op==1)
{
int x=rd()%m+1;
while(vs[x]) x=rd()%m+1;
printf("%d %d\n",st[x].fi,st[x].se); vs[x]=1; cnt++;
}
else if(op==2)
{
int x=rd()%n+1;
printf("%d %d\n",x,rd()%10+1);
}
else printf("%d %d\n",rd()%n+1,rd()%n+1);
}
return 0;
}
ARC187B
www,想了一辈子才做出来 \(Q \omega Q\)。
首先发现性质,一个连通块一定是区间上连续一段,简单分讨。
所以可以枚举区间左端点,寻找左端点的性质。
既然不属于前面的连通块,那么后缀最大值必须小于前缀最小值。
这样贡献可以对每个点进行统计,枚举前缀最小值作为上界就行了。
code
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 2e3+5,mod = 998244353;
int n,a[N],m,sp[N],ss[N],mi[N],mx[N];
LL p[N][N];
int main()
{
// freopen("in.in","r",stdin);
// freopen("out.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int j=1;j<=max(n,m);j++)
{
p[j][0]=1;
for(int i=1;i<=max(n,m);i++) p[j][i]=p[j][i-1]*j%mod;
}
p[0][0]=1;
mi[0]=m+1;
for(int i=1;i<=n;i++)
{
if(a[i]==-1) mi[i]=mi[i-1],sp[i]=sp[i-1]+1;
else mi[i]=min(mi[i-1],a[i]),sp[i]=sp[i-1];
}
for(int i=n;i>=1;i--)
{
if(a[i]==-1) mx[i]=mx[i+1],ss[i]=ss[i+1]+1;
else mx[i]=max(mx[i+1],a[i]),ss[i]=ss[i+1];
}
LL ans=0;
for(int i=1;i<=n;i++)
{
if(mx[i]>=mi[i-1]) continue;
for(int j=mx[i]+1;j<=m;j++)
{
LL tmp=p[m-j+1][sp[i-1]]-p[m-j][sp[i-1]];
ans=(ans+tmp*p[min(j,mi[i-1])-1][ss[i]])%mod;
}
if(!sp[i-1]) ans=(ans+p[mi[i-1]-1][ss[i]])%mod;
}
printf("%lld\n",ans);
return 0;
}
浙公网安备 33010602011771号