NOIP0805模拟赛题解
T1.旅行
大致题意:有一个图,第 \(i\) 次,删除第 \(i\) 个点,或者删除第 \(i\) 个点,并将他的所有邻居连起来。
每次询问可以通过一组路径相互到达的结点对数。
对于询问的内容,由于是无向图,只有在同一个连通块中的节点才能互相到达。
假设有 \(t\) 个连通块,设第 \(i\) 个连通块的大小为 \(sz_i\) ,于是一个连通块中满足要求的点对数量有 \(\frac{(sz_i-1)sz_i}{2}\) 。
于是问题转化成 \(\sum{\frac{(sz_i-1)sz_i}{2}}\) 。
对于本题的操作,我们应该可以联想到一个经典的问题。
如果将第二种操作去掉,我们只需要将整个过程倒过来,把删点变成加点,用并查集维护即可。
那么我们是否可以用类似的方法处理?
考虑第二个操作,容易发现,这个操作不会影响其所在连通块的连通性,只会使连通块大小 \(-1\) 。
于是,我们可以认为第二个操作就是将该点所在连通块大小 \(-1\) ,倒过来也就是 \(+1\) 。
由此,我们可以倒序处理,一开始,只存在要进行操作二的点,他们的连通块大小为0。
倒叙操作时,对于操作一,我们将那个点加入,将连通块大小设置为1,然后加边。
对于操作二,我们将那个点所在的连通块大小加一。
每次连通块大小改变时,减去原来连通块的大小的贡献,加上新的连通块大小的贡献。
对于第一个部分分,考虑每次,对于 \(s_i=0\) ,暴力删除点,以及点周围的边,对于 \(s_i=1\) ,将点的权值置为0(点初始权值为1),每次用dfs计算每个联通块的权值之和。
对于第二个部分分,将整个过程倒过来,每次加入一个点,连上边,用并查集统计每个区块的大小即可。
T2.造山
大致题意:对于一个序列,对于每个元素每次操作可以 \(+1\) 或 \(-1\) ,求最少多少次操作可以使得原序列严格上升。
首先,有一个很常见的trick,那就是对于严格上升序列,或者类似的各种严格的大于或者等于这些东西。
我们可以令 \(a_i-=i\) 。这样就将严格的,转换成不严格上升序列。
我们可以观察得出一个结论,修改后的数组的数集,应该是原数组的数集的子集,这个可以用调整法证明。
然后我们对数组离散化,就可以进行dp。
令 \(f[i][j]\) 表示前 \(i\) 个数,将第 \(i\) 个数修改为 \(j\) 并且满足不严格上升最小需要几次修改。
\(f[i][j] = \min(f[i][j],f[i-1][1\dots j])\)
这是一个 \(O(n^3)\) 的东西。
但发现,只需要对其进行一个前缀min的优化即可到 \(O(n^2)\)
(这题有\(O(n\log n)\)的做法,需要slope trick,可以自行学习)
对于第一个部分分,令 \(a_i\) 变为 \(a_1+abs(i-n/2+1)\) (此处 \(a_1\) 是原来的 \(a_1\))
对于第二个部分分,令 \(a_i\) 减 \(i\) ,变为求不下降的最小方案数,于是所有数变为同一个,全部变为 \(a_{\frac{n+1}{2}}\) 算一下就行。
T3.星际大战
大致题意:在强联通有向图中,是否存在超过 \(20\%\) 的到其他任意节点的简单路径唯一的点,如果超过,那么输出有哪些点
首先,考虑如何进行暴力,20分只需要枚举起点,然后用一个状压dp计算他到其他点的简短路径的数量。
考虑如何优化,我们的算法瓶颈在于判断一个点是否危险。
容易想到(出题人真的想到了),可以用dfs树来判断。
具体地,如果以一个点为根,建立dfs树,如果没有横叉边和前向边,那么这个点是危险的。
由此我们可以 \(O(n)\) 判断一个点是否是危险的,总体上 \(O(n^2)\) 可以完成。
观察题目,注意到 \(20\%\) 这个神秘东西。
发现,我们可以随机一个点,判断他是否危险。
随机 \(t\) 次,当危险点的数量超过 \(20\%\) ,找不到的概率为 \(0.8^t\),\(t\) 较大时(比如100),这个概率很小。
于是,我们可以用 \(O(100n)\) 的复杂度找到一个危险的点。
接下来,我们要考虑怎么用一个危险的点算出其他所有危险的点。
对于 \(v\) 点,当且仅当 \(v\) 的子树中有且仅有一条连向 \(v\) 祖先的返祖边,并且其连向的点 %u% 是危险的点。此时 \(v\) 也是危险的点。
我们只要对于每条 \(u\) 到 \(v\) 的返祖边,将 \(u\) 到 \(v\) 的点的权值加一(可以用树上差分做),即可解决第一个限制"当且仅当 \(v\) 的子树中有且仅有一条连向 \(v\) 祖先的返祖边"
接着,我们进行dfs,对于每个权值为1的点,其是否是危险的点,等价于,其子树内返祖边到达的点是否是危险的点。
T4.你的世界
考虑分析一个人的行为。一个人可以捡起经过的所有位置上的物,而它经过的所有位置是一段区间。并且,经过了整个区间,当且仅当它经过了区间的左右端点,因此固定区间时一个人的代价很容易计算出来。
考虑分析所有人的行为。通过调整法可以发现,可以认为任意两个人的区间都是不交的。
考虑一个很暴力的转化:把数轴按照人和物初始所在的位置分段,则最优解中每一段中所有边经过的次数相等,且均为 \(0,1,2\) 。因此直接枚举的状态量非常少。
考虑判定一个 \(0,1,2\) 的方案是否合法。考虑找充要条件。列几个条件可以发现,合法当且仅当:
物两侧两条线段不同时为 \(0\) ,不分别为 \(1\) 和 \(2\) 。
人两侧两条线段不同时为 \(1\) ,不同时为 \(2\) 。
每个非 \(0\) 的段至少经过一头人。
用这个充要条件容易写出DP。设 \(f_{i,j}\) ( \(j=0 \dots 4\) )
表示:考虑了与前 \(i\) 个边,
\(j=0\):最后一段未经过人,最后一条边权值为 1。
\(j=1\):最后一段未经过人,最后一条边权值为 2。
\(j=2\):最后一段已经过人,最后一条边权值为 1。
\(j=3\):最后一段已经过人,最后一条边权值为 2。
\(j=4\):最后一条边权值为 0。
#include<bits/stdc++.h>
#define ll long long
#define N 400009
#define mp make_pair
#define fi first
#define se second
using namespace std;
ll f[N][5];
pair<ll,bool> pos[N];
int n,m,tot;
ll M;
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>M>>n>>m;
for(int i=1;i<=n;i++){
ll x,y;
cin>>x>>y;
for(ll j=x;j<=y;j+=M)pos[++tot]=mp(j,1);
}
for(int i=1;i<=m;i++){
ll x,y;
cin>>x>>y;
for(ll j=x;j<=y;j+=M)pos[++tot]=mp(j,0);
}
sort(pos+1,pos+tot+1);
memset(f,0x3f,sizeof(f));
if(pos[1].se)f[1][3]=f[1][4]=0;else f[1][0]=0;;
for(int i=2;i<=tot;i++){
ll w=pos[i].fi-pos[i-1].fi;
if(!pos[i].se){
f[i][0]=min(f[i-1][3],f[i-1][4]);
f[i][1]=min(f[i-1][1],f[i-1][0])+w;
f[i][2]=min(f[i-1][2],f[i-1][0])+(w<<1);
f[i][3]=f[i-1][3]+w;
f[i][4]=f[i-1][4]+(w<<1);
}
else{
f[i][3]=min(min(f[i-1][2],f[i-1][0])+(w<<1),min(f[i-1][3],f[i-1][4]));
f[i][4]=min(min(f[i-1][1],f[i-1][0])+w,min(f[i-1][3],f[i-1][4]));
}
// for(int j=0;j<=4;j++)cout<<f[i][j]<<" ";cout<<"\n";
}
cout<<min(f[tot][3],f[tot][4]);
return 0;
}
满分做法不要求掌握。
首先可以将这个dp转移的方程改成 \((min,+)\) 矩阵乘法。
于是我们要维护的是 \(1e18\) 个矩阵的乘积。
如果理解矩阵的意义,应该是可以注意到,一个不含人或物端点的区间,每个位置对应的矩阵有一个长为 \(m\) 的周期。
然后就只需要维护每个周期的矩阵,这个可以用线段树来维护,然后在对每个端点维护即可。
就可以获得满分。
#include<bits/stdc++.h>
using namespace std;
#define i128 __int128
#define ll long long
#define ull unsigned long long
#define pii pair<int,int>
#define mp make_pair
#define fi first
#define se second
#define lowbit(x) ((x)&(-x))
#define It set<int>::iterator
//#define ls (rt<<1)
//#define rs (rt<<1|1)
//#define mid (l+r>>1)
//#define lson tr[rt].ls
//#define rson tr[rt].rs
//#define ls1 tr[rt1].ls
//#define rs1 tr[rt1].rs
//#define ls2 tr[rt2].ls
//#define rs2 tr[rt2].rs
#define pb emplace_back
const int mod=998244353;
int ksm(int x,int y){
int res=1;
while(y){
if(y&1)res=1LL*res*x%mod;
x=1LL*x*x%mod;
y>>=1;
}
return res;
}
int n,m;
char s[200005];
struct edge{
int v,nx;
}e[800005];
int cnt,hd[200005];
void add(int u,int v){
e[++cnt]=edge{v,hd[u]};
hd[u]=cnt;
}
void add_edge(int u,int v){
add(u,v);add(v,u);
}
int fa[200005],sz[200005];
bool vis[200005];
ll res=0;
ll calc(int x){
return (ll)x*(x-1)/2;
}
void init(){
for(int i=1;i<=n;i++)fa[i]=i;
}
int find(int x){
if(x==fa[x])return x;
else return fa[x]=find(fa[x]);
}
void merge(int x,int y){
int fx=find(x),fy=find(y);
if(fx==fy)return;
res-=calc(sz[fx]);res-=calc(sz[fy]);
res+=calc(sz[fx]+sz[fy]);
sz[fx]+=sz[fy];fa[fy]=fx;
}
vector<ll> ans;
int main(){
scanf("%d%d",&n,&m);init();
scanf("%s",s+1);
for(int i=1;i<=m;i++){
int u,v;scanf("%d%d",&u,&v);
add_edge(u,v);
}
for(int u=1;u<=n;u++)
if(s[u]=='1'){
vis[u]=1;
for(int i=hd[u];i;i=e[i].nx){
int v=e[i].v;
if(vis[v])merge(u,v);
}
}else sz[u]=1;
for(int u=n;u>=1;u--){
if(s[u]=='0'){
vis[u]=1;
for(int i=hd[u];i;i=e[i].nx){
int v=e[i].v;
if(vis[v])merge(u,v);
}
}else{
int fu=find(u);
res-=calc(sz[fu]);
sz[fu]++;
res+=calc(sz[fu]);
}
ans.pb(res);
}
reverse(ans.begin(),ans.end());
for(ll as:ans)printf("%lld\n",as);
return 0;
}
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int n, a[3005];
ll f[3005][3005];
int b[3005], cnt;
map<int, int> p;
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]), a[i] -= i, b[i] = a[i];
sort(b + 1, b + 1 + n);
for (int i = 1; i <= n; i++)
if (b[i] != b[i - 1])
b[++cnt] = b[i], p[b[cnt]] = cnt;
for (int i = 1; i <= n; i++)
a[i] = p[a[i]];
memset(f, 0x3f, sizeof(f));
for (int i = 0; i <= n; i++)
f[0][i] = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= cnt; j++)
f[i][j] = f[i - 1][j] + abs(b[j] - b[a[i]]);
for (int j = 1; j <= cnt; j++)
f[i][j] = min(f[i][j], f[i][j - 1]);
}
ll res = f[n][1];
for (int i = 1; i <= cnt; i++)
res = min(res, f[n][i]);
printf("%lld\n", res);
return 0;
}
#include <bits/stdc++.h>
namespace mystd {
inline int read() {
char c = getchar();
int x = 0, f = 1;
while (c < '0' || c > '9') f = (c == '-') ? -1 : f, c = getchar();
while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + c - '0', c = getchar();
return x * f;
}
inline void write(int x) {
if (x < 0) x = ~(x - 1), putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
}
using namespace std;
using namespace mystd;
const int maxn = 1e5 + 1000;
const int maxm = 3e5 + 2000;
struct edge { int to, nxt; } e[maxm];
int T, n, m, tot, fl, head[maxn], in[maxn], vis[maxn], cnt[maxn], pr[maxn], dep[maxn];
vector<int> g[maxn], tr[maxn], ans, tp;
void add_edge(int u, int v) {
e[++tot] = (edge) { v, head[u] };
head[u] = tot;
}
void dfs(int u) {
in[u] = 1, vis[u] = 1;
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if (vis[v] && !in[v]) fl = 1;
else if (!in[v]) dep[v] = dep[u] + 1, dfs(v), tr[u].push_back(v);
else g[u].push_back(v), pr[u] = (dep[v] > dep[pr[u]]) ? pr[u] : v;
}
in[u] = 0;
}
bool check(int u) {
for (int i = 1; i <= n; i++) g[i].clear(), tr[i].clear(), cnt[i] = vis[i] = in[i] = pr[i] = 0;
fl = 0, dep[u] = 1, dep[0] = n + 1;
dfs(u);
return !fl;
}
void calc(int u) {
for (int v : tr[u]) {
calc(v), cnt[u] += cnt[v];
if (dep[pr[u]] > dep[pr[v]]) pr[u] = pr[v];
}
}
void solv(int u) {
if (cnt[u] == 1 && dep[pr[u]] < dep[u] && vis[pr[u]]) vis[u] = 1;
for (int v : tr[u]) solv(v);
}
void solve() {
n = read(), m = read(), tot = 0;
for (int i = 1; i <= n; i++) cnt[i] = head[i] = 0;
ans.clear(), tp.clear();
for (int i = 1; i <= n; i++) tp.push_back(i);
for (int i = 1, u, v; i <= m; i++) {
u = read(), v = read();
add_edge(u, v);
}
int rt = -1;
random_shuffle(tp.begin(), tp.end());
for (int i = 0; i <= tp.size() - 1 && i < 100; i++) {
if (check(tp[i])) {
rt = tp[i];
break;
}
}
if (rt == -1) return puts("YES"), void();
for (int u = 1; u <= n; u++) for (int v : g[u]) cnt[u]++, cnt[v]--;
calc(rt);
for (int i = 1; i <= n; i++) vis[i] = 0;
vis[rt] = 1;
solv(rt);
for (int i = 1; i <= n; i++) if (vis[i]) ans.push_back(i);
if ((int)ans.size() * 5 < n) puts("YES");
else{
puts("NO");
for (int i : ans) write(i), putchar(' ');
puts("");
}
}
int main() {
srand((unsigned)time(0));
T = read();
while (T--) solve();
return 0;
}
#include<bits/stdc++.h>
#define ll long long
#define N 400009
#define mp make_pair
#define fi first
#define se second
using namespace std;
ll f[N][5];
pair<ll,bool> pos[N];
int n,m,tot;
ll M;
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>M>>n>>m;
for(int i=1;i<=n;i++){
ll x,y;
cin>>x>>y;
for(ll j=x;j<=y;j+=M)pos[++tot]=mp(j,1);
}
for(int i=1;i<=m;i++){
ll x,y;
cin>>x>>y;
for(ll j=x;j<=y;j+=M)pos[++tot]=mp(j,0);
}
sort(pos+1,pos+tot+1);
memset(f,0x3f,sizeof(f));
if(pos[1].se)f[1][3]=f[1][4]=0;else f[1][0]=0;;
for(int i=2;i<=tot;i++){
ll w=pos[i].fi-pos[i-1].fi;
if(!pos[i].se){
f[i][0]=min(f[i-1][3],f[i-1][4]);
f[i][1]=min(f[i-1][1],f[i-1][0])+w;
f[i][2]=min(f[i-1][2],f[i-1][0])+(w<<1);
f[i][3]=f[i-1][3]+w;
f[i][4]=f[i-1][4]+(w<<1);
}
else{
f[i][3]=min(min(f[i-1][2],f[i-1][0])+(w<<1),min(f[i-1][3],f[i-1][4]));
f[i][4]=min(min(f[i-1][1],f[i-1][0])+w,min(f[i-1][3],f[i-1][4]));
}
// for(int j=0;j<=4;j++)cout<<f[i][j]<<" ";cout<<"\n";
}
cout<<min(f[tot][3],f[tot][4]);
return 0;
}
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3fll
#define N 40009
#define ll long long
#define ls (rt<<1)
#define rs (rt<<1|1)
using namespace std;
struct mat{
ll a[5][5];
void init(){memset(a,0x3f,sizeof(a));}
mat operator*(const mat b){
mat ret;ret.init();
for(int i=0;i<5;i++){
for(int j=0;j<5;j++){
for(int k=0;k<5;k++)ret.a[i][k]=min(ret.a[i][k],a[i][j]+b.a[j][k]);
}
}
return ret;
}
} tr[N<<2];
mat get(int x,ll y){
mat ret;ret.init();
if(x==2){
for(int i=0;i<=4;i++)ret.a[i][i]=0;
}
else if(x==1){
ret.a[3][0]=y<<1,ret.a[3][2]=y<<1,ret.a[4][0]=y,ret.a[4][1]=y;
ret.a[3][3]=0,ret.a[3][4]=0,ret.a[4][3]=0,ret.a[4][4]=0;
}
if(!x){
ret.a[0][3]=ret.a[0][4]=0;
ret.a[1][0]=ret.a[1][1]=y;
ret.a[2][0]=ret.a[2][2]=y<<1;
ret.a[3][3]=y;
ret.a[4][4]=y<<1;
}
return ret;
}
inline void up(int rt){tr[rt]=tr[rs]*tr[ls];}
void build(int rt,int l,int r){
tr[rt]=get(2,0);
if(l!=r){
int mid=(l+r>>1);
build(ls,l,mid);
build(rs,mid+1,r);
}
}
void upd(int rt,int l,int r,int x,mat v){
if(l==r)tr[rt]=v;
else{
int mid=(l+r>>1);
if(x<=mid)upd(ls,l,mid,x,v);
else upd(rs,mid+1,r,x,v);
up(rt);
}
}
mat qry(int rt,int l,int r,int ql,int qr){
if(ql>r||qr<l)return get(2,0);
else if(ql<=l&&qr>=r)return tr[rt];
else{
int mid=(l+r>>1);
return qry(rs,mid+1,r,ql,qr)*qry(ls,l,mid,ql,qr);
}
}
ll M;int n,m,tot;
struct node1{
int typ;
ll l,r;
bool operator<(const node1 &b){
return l%M<b.l%M;
}
} a[N];
struct node2{
int id,typ;
ll pos;
bool operator<(const node2 &b){
return pos<b.pos||(pos==b.pos&&id<b.id);
}
} b[N<<1];
set<int> st;
mat qpow(mat x,ll y){
mat z=get(2,0);
while(y){
if(y&1)z=z*x;
x=x*x;y>>=1;
}
return z;
}
ll ps[N];
ll d[N];int tp[N];
bool is[N];
mat sum(ll x,int idx,ll y,int idy){
if(x>y||x==y&&idx>idy)return get(2,0);
if(x==y){
return qry(1,1,n,idx,idy);
}
else if(x==y-1){
return qry(1,1,n,1,idy)*qry(1,1,n,idx,n);
}
else{
return qry(1,1,n,1,idy)*qpow(tr[1],y-x-1)*qry(1,1,n,idx,n);
}
}
inline void findnxt(ll &x,int &y){
if(y==n){++x,y=1;}
else ++y;
}
inline void findlst(ll &x,int &y){
if(y==1){--x,y=n;}
else --y;
}
inline ll getpos(ll x,int y){
return x*M+ps[y];
}
ll ans[5]={INF,INF,INF,INF,INF};
void add(mat tmp){
for(int j=0;j<5;j++){
for(int k=0;k<5;k++){
tmp.a[j][k]+=ans[k];
}
}
for(int j=0;j<5;j++){
ans[j]=INF;
for(int k=0;k<5;k++){
ans[j]=min(ans[j],tmp.a[j][k]);
}
}
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>M>>n>>m;
for(int i=1;i<=n;i++){
ll x,y;cin>>x>>y;
a[++tot]=node1{1,x,y};
}
for(int i=1;i<=m;i++){
ll x,y;cin>>x>>y;
a[++tot]=node1{0,x,y};
}sort(a+1,a+tot+1);
for(int i=1;i<=tot;i++){
ps[i]=a[i].l%M;
b[i*2-1]=node2{i,a[i].typ,a[i].l};
b[i<<1]=node2{i,a[i].typ,a[i].r+M};
}n=tot;tot<<=1;sort(b+1,b+tot+1);
b[0]=b[1];build(1,1,n);ll lst=b[1].pos/M;int lid=b[1].id;
if(b[1].typ)ans[3]=ans[4]=0;else ans[0]=0;
st.insert(lid);upd(1,1,n,lid,get(b[1].typ,M));d[lid]=M;
is[lid]=1;tp[lid]=b[1].typ;
for(int i=2;i<=tot;i++){
int nid=b[i].id;ll now=b[i].pos/M;
findlst(now,nid);findnxt(lst,lid);
add(sum(lst,lid,now,nid));findnxt(now,nid);findlst(lst,lid);
if(is[nid]){
st.erase(nid);
upd(1,1,n,nid,get(2,0));
if(!st.empty()){
auto it=st.upper_bound(nid);
if(it==st.end())it=st.begin();
int t=*it;
d[t]+=d[nid];
upd(1,1,n,t,get(tp[t],d[t]));
}
d[nid]=tp[nid]=0;
}
else{
d[nid]=ps[nid];tp[nid]=b[i].typ;
bool flag=0;
if(!st.empty()){
auto it=st.lower_bound(nid);
if(it==st.begin())it=st.end(),d[nid]+=M;--it;
d[nid]-=ps[*it];
}else d[nid]=M,flag=1;
if(!st.empty()){
auto it=st.upper_bound(nid);
if(it==st.end())it=st.begin();
int t=*it;
d[t]-=d[nid];
upd(1,1,n,t,get(tp[t],d[t]));
}
if(flag)add(get(tp[nid],getpos(now,nid)-getpos(lst,lid)+M));
else add(get(tp[nid],d[nid]));
upd(1,1,n,nid,get(tp[nid],d[nid]));
st.insert(nid);is[nid]=1;
}
lid=nid;lst=now;
}
cout<<min(ans[3],ans[4])<<"\n";
return 0;
}
浙公网安备 33010602011771号