首页 > 其他分享 >luogu P1963 [NOI2009] 变换序列

luogu P1963 [NOI2009] 变换序列

时间:2023-06-17 19:33:36浏览次数:39  
标签:return int luogu linky NOI2009 ed 匹配 P1963 define

luogu P1963 [NOI2009] 变换序列

题意

对于\(N\)个整数\(0, 1, \cdots, N-1\),一个变换序列\(T\)可以将\(i\)变成\(T_i\),其中 \(T_i \in \{ 0,1,\cdots, N-1\}\) 且 \(\bigcup_{i=0}^{N-1} \{T_i\} = \{0,1,\cdots , N-1\}\)。 ,\(\forall x,y \in \{0,1,\cdots , N-1\}\),定义x和y之间的距离\(D(x,y)=min\{|x-y|,N-|x-y|\}\) 。给定每个\(i\)和\(T_i\)之间的距离\(D(i,T_i)\),你需要求出一个满足要求的变换序列T。如果有多个满足条件的序列,输出其中字典序最小的一个。

说明:对于两个变换序列\(S\)和\(T\),如果存在\(p<N\),满足对于\(i=0,1,\cdots p-1\),\(S_i=T_i\)且\(S_p<T_p\),我们称\(S\)比\(T\)字典序小。

题解

难受啊。但感觉这题还是不错的。
我们知道了 \(D(i,T_i)\) 其实就已经很有启发感了,这并不是一个很难的方程。
解完方程之后我们会发现每一个 \(T_i\) 都有大概四种可能。
题目要求的是每一个数都只能使用一次,这里其实暗示的已经很明显了。
我们将 \(i\) 作为左部,\(T_i\) 作为右部,每一对其实就是一对匹配,有解就是有完美匹配。
有解的问题解决了,剩下的就是字典序。
我们想一下最大匹配的过程,由于是按顺序找增广路的,撤销只会撤销前面已经匹配的点。
所以有一个显然的想法,将左部的连边从小到大排序,然后再倒序匹配,这样乍一看好像是对的。
因为前面的优先级大于后面的,为了让前面的尽量小我们只会牺牲后面的,而后面的再大也没什么关系。
这个结论在这个题中是对的,在一般二分图中就不对了。 这里放一个偷的图。

错在哪里呢?这个策略的贪心只是保证了当前位最优,而后面的撤销不能保证是最优的
我们不妨将思路换一换,在一些字典序最小的题中有一个经典的操作,在保证有解的情况下按位依次尽量小

这题中我们就可以这样操作。
还是将所有出边从小到大排序,按顺序依次寻找增广路,将当前这条边对应的两点删掉,跑一边最大匹配,若依然有解则不添回来,否则添回来然后继续寻找增广路,这样做的时间复杂度是 \(O(m^2n)\) 的。
但实际上还有优化的空间,我们每次只用判断有无解,而删除一条边并不会影响全局,只会影响两端的和其有关的边。
所以我们可以一开始就跑一遍最大匹配,然后和上面一样填数,如果要修改一条边,我们只需要考虑修改到这里的边原来对应的左部的点能不能找到一条增广路就行了,这足以判断有没有最大匹配。 然后和上面一样判断删不删就行了,时间复杂度 \(O(n^2m)\) ,

code

#include <bits/stdc++.h>
#define rg register
#define pc putchar
#define gc getchar
#define pf printf
#define space pc(' ')
#define enter pc('\n')
#define me(x,y) memset(x,y,sizeof(x))
#define pb push_back
#define FOR(i,k,t,p) for(rg int i(k) ; i <= t ; i += p)
#define ROF(i,k,t,p) for(rg int i(k) ; i >= t ; i -= p)
using namespace std ;
bool s_gnd ;
inline void read(){}
template<typename T,typename ...T_>
inline void read(T &x,T_&...p)
{
    x = 0 ;rg int f(0) ; rg char c(gc()) ;
    while(!isdigit(c)) f |= (c=='-'),c = gc() ;
    while(isdigit(c)) x = (x<<1)+(x<<3)+(c^48),c = gc() ;
    x = (f?-x:x) ;
    read(p...);
}
int stk[30],tp ;
inline void print(){}
template<typename T,typename ...T_>
inline void print(T x,T_...p)
{
    if(x < 0) pc('-'),x = -x ;
    do stk[++tp] = x%10,x /= 10 ; while(x) ;
    while(tp) pc(stk[tp--]^48) ; space ;
    print(p...) ;
}
const int N = 2e4+5 ;
int n,ans ;
int d[N],T[N],linkx[N],linky[N],ed[N],vis[N] ;
vector<int>e[N] ;
bool S_GND ;
int Dfs(int x)
{
    if(vis[x] || ed[x]) return 0 ; vis[x] = 1 ;
    for(auto v:e[x]) if(!ed[v+n] && (!linky[v] || Dfs(linky[v])))
    {
        linkx[x] = v,linky[v] = x ; return 1 ;
    } return 0 ;
}
signed main()
{
//cerr<<(double)(&s_gnd-&S_GND)/1024.0/1024.0 ;
//	freopen(".in","r",stdin) ;
//	freopen(".out","w",stdout) ;
    read(n) ;
    FOR(i,1,n,1) read(d[i]) ;
    FOR(i,1,n,1)
    {
        if(i+d[i] <= n) e[i].pb(i+d[i]) ;
        if(i-d[i] >= 1) e[i].pb(i-d[i]) ;
        if(n+i-d[i] <= n) e[i].pb(n+i-d[i]) ;
        if(i+d[i]-n >= 1) e[i].pb(i+d[i]-n) ;
        sort(e[i].begin(),e[i].end()) ; 
        if(d[i] > n/2) {puts("No Answer") ; return 0 ;}
    }
    FOR(i,1,n,1) me(vis,0),ans += Dfs(i) ;
    if(ans < n) {puts("No Answer") ; return 0 ;}
    FOR(i,1,n,1) for(auto v:e[i])
    {
        int rp = 0 ;
        if(linkx[i] == v) rp = 1 ;
        else 
        {
            ed[i] = ed[v+n] = 1,linky[linkx[i]] = 0,me(vis,0) ;
            if(Dfs(linky[v])) rp = 1 ;
            else linky[linkx[i]] = i ; ed[i] = ed[v+n] = 0 ;
        }
        if(rp) {ed[i] = ed[v+n] = 1 ; print(v-1) ; break ;}
    }
    return 0 ;
}

...

总结一下这题有些什么trick。
1.关于字典序的题目可能可以在保证满足条件的情况下填位。
2.最大匹配修改边看有无解只需要判断修改的点能不能搜出增广路,这样类似的思想应该也能推广到其它题中。

标签:return,int,luogu,linky,NOI2009,ed,匹配,P1963,define
From: https://www.cnblogs.com/snow-panther/p/17488105.html

相关文章

  • Luogu3792 由乃与大母神原型和偶像崇拜 - 线段树 - set -
    题目链接:https://www.luogu.com.cn/problem/P3792题解:一点小小的空间震撼(ML:125MB)首先询问可以转化成:区间\([l,r]\)的\(max-min+1=r-l+1\)并且区间内没有重复元素。可以考虑对每个点\(i\)记一个前驱结点\(pr_i\),表示\(a_{pr_i}=a_i\),且\(pr_i\)是\(i\)前面离\(i\)......
  • Luogu P1495 【模板】中国剩余定理(CRT)/ 曹冲养猪
    【模板】中国剩余定理(CRT)/曹冲养猪题目描述自从曹冲搞定了大象以后,曹操就开始捉摸让儿子干些事业,于是派他到中原养猪场养猪,可是曹冲满不高兴,于是在工作中马马虎虎,有一次曹操想知道母猪的数量,于是曹冲想狠狠耍曹操一把。举个例子,假如有\(16\)头母猪,如果建了\(3\)个猪圈,剩下......
  • luogu P7740 [NOI2021] 机器人游戏
    题面传送门一个bitset值52分?首先样例让你容斥你就容斥,枚举哪些位是可以的,计算每一位的\(p_0,p_1,q_0,q_1\)表示是否被要求最后是\(0/1\),是否有最终值是开始值异或\(0/1\)。然后每个位置的贡献可以分类讨论出来:如果\(p_0,p_1\)或者\(q_0,q_1\)都有,那只能空着。如......
  • Luogu P7118
    题面题意很清楚,就不复述了。不难发现,我们首先要求出场景数小于给定galgame的galgame数量,于是我们需要求出场景数\(=i\)的galgame数量,设为\(f_i\)。考虑根节点,当A场景大小为\(j\)时,B场景的大小为\(i-j-1\)。枚举每个\(j\),不难得到\(f_i=\sum\limits_{j=0}^{i-1......
  • Luogu P2580 于是他错误的点名开始了
    于是他错误的点名开始了题目背景XS中学化学竞赛组教练是一个酷爱炉石的人。他会一边搓炉石一边点名以至于有一天他连续点到了某个同学两次,然后正好被路过的校长发现了然后就是一顿欧拉欧拉欧拉(详情请见已结束比赛CON900)。题目描述这之后校长任命你为特派探员,每天记录他的点......
  • Luogu P3435 [POI2006] OKR-Periods of Words
    [POI2006]OKR-PeriodsofWords题面翻译对于一个仅含小写字母的字符串\(a\),\(p\)为\(a\)的前缀且\(p\nea\),那么我们称\(p\)为\(a\)的proper前缀。规定字符串\(Q\)(可以是空串)表示\(a\)的周期,当且仅当\(Q\)是\(a\)的proper前缀且\(a\)是\(Q+Q\)的前缀......
  • Luogu P2375 [NOI2014] 动物园
    [NOI2014]动物园题目描述近日,园长发现动物园中好吃懒做的动物越来越多了。例如企鹅,只会卖萌向游客要吃的。为了整治动物园的不良风气,让动物们凭自己的真才实学向游客要吃的,园长决定开设算法班,让动物们学习算法。某天,园长给动物们讲解KMP算法。园长:“对于一个字符串\(S\),它......
  • Luogu P4824 [USACO15FEB] Censoring S
    [USACO15FEB]CensoringS题面翻译FarmerJohn为他的奶牛们订阅了GoodHooveskeeping杂志,因此他们在谷仓等待挤奶期间,可以有足够的文章可供阅读。不幸的是,最新一期的文章包含一篇关于如何烹制完美牛排的不恰当的文章,FJ不愿让他的奶牛们看到这些内容。FJ已经根据杂志的所有文字,......
  • Luogu P3375 【模板】KMP字符串匹配
    【模板】KMP字符串匹配题目描述给出两个字符串\(s_1\)和\(s_2\),若\(s_1\)的区间\([l,r]\)子串与\(s_2\)完全相同,则称\(s_2\)在\(s_1\)中出现了,其出现位置为\(l\)。现在请你求出\(s_2\)在\(s_1\)中所有出现的位置。定义一个字符串\(s\)的border为\(s\)......
  • Luogu P3167 [CQOI2014]通配符匹配
    [CQOI2014]通配符匹配题目描述几乎所有操作系统的命令行界面(CLI)中都支持文件名的通配符匹配以方便用户。最常见的通配符有两个,一个是星号(”“'),可以匹配0个及以上的任意字符:另一个是问号(”?“),可以匹配恰好一个任意字符。现在需要你编写一个程序,对于给定的文件名列表和一个包......