AtCoder Peterzavodsk Contest 001 题解

A. Two integers

求是 x x 的倍数且不是 y y 的倍数的一个数 z z ,要求 z z 不超过 1018 10 18

#include

#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-funroll-loops")
#pragma GCC target("avx,sse4")

#define mms(a,n) memset(a,0,sizeof((a)[0])*(n))
#define mmp(a,b,n) memcpy(a,b,sizeof((b)[0])*(n))
#define lowbit(x) ((x)&-(x))
#define pb push_back
#define fi first
#define se second
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define fo(i,l,r) for(register int i=l,_lim_=r;i<=_lim_;i++)
#define fd(i,r,l) for(register int i=r,_lim_=l;i>=_lim_;i--)
#define fos(i,l,r,d) for(register int i=l,_lim_=r;i<=r;i+=d)
#define fol(i,l,r) for(register ll i=l,_lim_=r;i<=_lim_;i++)
#define fdl(i,r,l) for(register ll i=r,_lim_=l;i>=_lim_;i--)
#define fosl(i,l,r,d) for(register ll i=l,_lim_=r;i<=r;i+=d)
#define Clear(a) memset(a,0,sizeof(a))
#define Copy(a,b) memcpy(a,b,sizeof(b))
#define ALL(v) v.begin(),v.end()
#define SZ(v) ((int)v.size())

using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef long double ldb;
typedef double db;
typedef pair<int,int> pi;

int x,y;

int main(){
    cin>>x>>y;
    cout<<(x%y?x:-1);
    return 0;
}

B. Two Arrays

有两个数组 a a b b ,每次可以给 a a 中一个元素 +2 + 2 ,并同时给 b b 中一个元素 +1 + 1 ,问能否使得 a a b b 相同。范围 105 10 5

增加的次数是确定的,于是先加完 2 2 再补 1 1 ,只要 a a 中对应位置元素不小于 b b 中即可。

#include

#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-funroll-loops")
#pragma GCC target("avx,sse4")

#define mms(a,n) memset(a,0,sizeof((a)[0])*(n))
#define mmp(a,b,n) memcpy(a,b,sizeof((b)[0])*(n))
#define lowbit(x) ((x)&-(x))
#define pb push_back
#define fi first
#define se second
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define fo(i,l,r) for(register int i=l,_lim_=r;i<=_lim_;i++)
#define fd(i,r,l) for(register int i=r,_lim_=l;i>=_lim_;i--)
#define fos(i,l,r,d) for(register int i=l,_lim_=r;i<=r;i+=d)
#define fol(i,l,r) for(register ll i=l,_lim_=r;i<=_lim_;i++)
#define fdl(i,r,l) for(register ll i=r,_lim_=l;i>=_lim_;i--)
#define fosl(i,l,r,d) for(register ll i=l,_lim_=r;i<=r;i+=d)
#define Clear(a) memset(a,0,sizeof(a))
#define Copy(a,b) memcpy(a,b,sizeof(b))
#define ALL(v) v.begin(),v.end()
#define SZ(v) ((int)v.size())

using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef long double ldb;
typedef double db;
typedef pair<int,int> pi;

const int N=10005;
int n,a[N],b[N];
ll x,y;

int main(){
    scanf("%d",&n);
    fo(i,1,n)scanf("%d",a+i);
    fo(i,1,n)scanf("%d",b+i);
    fo(i,1,n)x+=max(b[i]-a[i]+1,0)>>1,y+=b[i]-a[i];
    if(y"No");else puts("Yes");
    return 0;
}

C. Vacant Seat

n105 n ≤ 10 5 个位置排成一个环,其中 n n 是奇数,在其中有数字 0,1,2 0 , 1 , 2 ,其中 0 0 1 1 都不能连续出现,不超过 20 20 次询问一个位置的值,找出一个 2 2 的位置。

讨论区间长度和左右填的数字可知其中是否一定出现 2 2 ,于是每次二分即可。

#include

#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-funroll-loops")
#pragma GCC target("avx,sse4")

#define mms(a,n) memset(a,0,sizeof((a)[0])*(n))
#define mmp(a,b,n) memcpy(a,b,sizeof((b)[0])*(n))
#define lowbit(x) ((x)&-(x))
#define pb push_back
#define fi first
#define se second
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define fo(i,l,r) for(register int i=l,_lim_=r;i<=_lim_;i++)
#define fd(i,r,l) for(register int i=r,_lim_=l;i>=_lim_;i--)
#define fos(i,l,r,d) for(register int i=l,_lim_=r;i<=r;i+=d)
#define fol(i,l,r) for(register ll i=l,_lim_=r;i<=_lim_;i++)
#define fdl(i,r,l) for(register ll i=r,_lim_=l;i>=_lim_;i--)
#define fosl(i,l,r,d) for(register ll i=l,_lim_=r;i<=r;i+=d)
#define Clear(a) memset(a,0,sizeof(a))
#define Copy(a,b) memcpy(a,b,sizeof(b))
#define ALL(v) v.begin(),v.end()
#define SZ(v) ((int)v.size())

using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef long double ldb;
typedef double db;
typedef pair<int,int> pi;

int n;

int Query(int u){
    static char ch[10];
    printf("%d\n",u);fflush(stdout);
    scanf("%s",ch);
    if(*ch=='V')exit(0);
    return *ch=='M'?1:0;
}
void solve(int l,int r,int s){
    int mi=(l+r+1)>>1,st=Query(mi),dif=st^s;
    if(dif!=((mi-l)&1))
        solve(l,mi-1,s);
    else
        solve(mi,r,st);
}

int main(){
    scanf("%d",&n);
    solve(0,n-1,Query(0));
    return 0;
}

D. Forest

给一个森林,每个点有权值 ai a i ,增加若干条边使得森林联通,且每个点最多与一条新增加的边相连。求有新增加的边相连的点的权值和的最小值。范围 105 10 5

显然每个联通块至少有一个点被计算,且所有联通块中共有 2m2 2 m − 2 个点被计算。于是删掉一个联通块中最小的元素算进答案中,剩下来按顺序加起来即可。时间复杂度可以做到 O(n) O ( n )

#include

#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-funroll-loops")
#pragma GCC target("avx,sse4")

#define mms(a,n) memset(a,0,sizeof((a)[0])*(n))
#define mmp(a,b,n) memcpy(a,b,sizeof((b)[0])*(n))
#define lowbit(x) ((x)&-(x))
#define pb push_back
#define fi first
#define se second
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define fo(i,l,r) for(register int i=l,_lim_=r;i<=_lim_;i++)
#define fd(i,r,l) for(register int i=r,_lim_=l;i>=_lim_;i--)
#define fos(i,l,r,d) for(register int i=l,_lim_=r;i<=r;i+=d)
#define fol(i,l,r) for(register ll i=l,_lim_=r;i<=_lim_;i++)
#define fdl(i,r,l) for(register ll i=r,_lim_=l;i>=_lim_;i--)
#define fosl(i,l,r,d) for(register ll i=l,_lim_=r;i<=r;i+=d)
#define Clear(a) memset(a,0,sizeof(a))
#define Copy(a,b) memcpy(a,b,sizeof(b))
#define ALL(v) v.begin(),v.end()
#define SZ(v) ((int)v.size())

using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef long double ldb;
typedef double db;
typedef pair<int,int> pi;

const int N=100005;
int n,m,a[N],fa[N],blk;
vector<int>e[N],E;
ll ans;

int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}

int main(){
    scanf("%d%d",&n,&m);
    fo(i,1,n)scanf("%d",&a[i]),fa[i]=i,blk++;
    for(int i=1,u,v;i<=m;i++){
        scanf("%d%d",&u,&v);u++;v++;
        if(find(u)!=find(v))fa[find(u)]=find(v),blk--;
    }
    fo(i,1,n)e[find(i)].pb(a[i]);
    if(blk==1)puts("0"),exit(0);
    int cur=blk-2;
    fo(i,1,n)if(e[i].size()){
        sort(ALL(e[i]));
        ans+=e[i][0];
        fo(j,1,int(e[i].size())-1)E.pb(e[i][j]);
    }
    sort(ALL(E));
    if(cur>E.size())puts("Impossible"),exit(0);
    fo(i,0,cur-1)ans+=E[i];
    cout<return 0;
}

E. Antennas on Tree

有一棵 n105 n ≤ 10 5 个点的树,无边权。你要在上面选 k k 个点,一个点的坐标由它到每个点的树上距离定义为一个 k k 维的坐标。求最小的 k k 使得坐标两两不同。

首先我们考虑在什么情况下坐标会相同。容易发现坐标相同意味着对于一个点来说,有两个不同的子树满足其中没有选择的点,这样这两个子树的根节点的坐标就相同了。
假设我们已经选出了 k k 个点,那么我们将所有在这 k k 个点两两路径上的所有点拿出来,这是这棵树的一个子联通块,也是一棵树。同时这棵树恰好有 k k 个叶子,也就是我们选出的点。容易发现,需要满足的条件就是未被选出的点是若干条链,且任意一个被选的点至多和一个未被选出的点相连。
也就是说我们现在要选择一个子联通块满足上面的条件,同时叶子尽量少。
于是我们对于每个原树中的叶子给一个标号,就是沿着叶子向上碰到的最近的度数 >2 > 2 的节点。我们删除未被选的节点的过程实际上就是把叶子到他的标号的这条链缩起来,然后这个标号就不能再缩了。
于是我们只要算有多少种标号就行了,时间复杂度 O(n) O ( n )
特判一下一条链的情况即可。

#include

#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-funroll-loops")
#pragma GCC target("avx,sse4")

#define mms(a,n) memset(a,0,sizeof((a)[0])*(n))
#define mmp(a,b,n) memcpy(a,b,sizeof((b)[0])*(n))
#define lowbit(x) ((x)&-(x))
#define pb push_back
#define fi first
#define se second
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define fo(i,l,r) for(register int i=l,_lim_=r;i<=_lim_;i++)
#define fd(i,r,l) for(register int i=r,_lim_=l;i>=_lim_;i--)
#define fos(i,l,r,d) for(register int i=l,_lim_=r;i<=r;i+=d)
#define fol(i,l,r) for(register ll i=l,_lim_=r;i<=_lim_;i++)
#define fdl(i,r,l) for(register ll i=r,_lim_=l;i>=_lim_;i--)
#define fosl(i,l,r,d) for(register ll i=l,_lim_=r;i<=r;i+=d)
#define Clear(a) memset(a,0,sizeof(a))
#define Copy(a,b) memcpy(a,b,sizeof(b))
#define ALL(v) v.begin(),v.end()
#define SZ(v) ((int)v.size())

using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef long double ldb;
typedef double db;
typedef pair<int,int> pi;

const int N=100005;
int n,m;bool vi[N];
vector<int>e[N];
int dfs(int u,int fa){
    if(e[u].size()>2)return u;
    for(int v:e[u])if(v!=fa)return dfs(v,u);
    return u;
}

int main(){
    scanf("%d",&n);
    for(int i=1,u,v;iscanf("%d%d",&u,&v),u++,v++,e[u].pb(v),e[v].pb(u);
    fo(i,1,n)if(e[i].size()==1)vi[dfs(i,0)]=true,m++;
    fo(i,1,n)if(vi[i])m--;if(!m)m++;
    printf("%d\n",m);
    return 0;
}

F. XOR Tree

给一棵 n105 n ≤ 10 5 个节点的树,每条边上有权值 vi15 v i ≤ 15 ,每次可以选择一条路径将路径上所有的边权值异或某个数 w w 。求最少多少次将所有边权变成 0 0

Vi V i 表示所有与 i i 相连的边权的异或和,于是每次就是将两个值异或上一个数 w w ,最后使得所有数为 0 0 。首先相同的肯定两两配对,令 dp[S] d p [ S ] 表示集合 S S 中的元素还有 1 1 个时的答案,每次显然会将一个元素异或为 0 0 ,于是直接 dp d p 即可。时间复杂度 O(n+224) O ( n + 2 24 )

#include

#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-funroll-loops")
#pragma GCC target("avx,sse4")

#define mms(a,n) memset(a,0,sizeof((a)[0])*(n))
#define mmp(a,b,n) memcpy(a,b,sizeof((b)[0])*(n))
#define lowbit(x) ((x)&-(x))
#define pb push_back
#define fi first
#define se second
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define fo(i,l,r) for(register int i=l,_lim_=r;i<=_lim_;i++)
#define fd(i,r,l) for(register int i=r,_lim_=l;i>=_lim_;i--)
#define fos(i,l,r,d) for(register int i=l,_lim_=r;i<=r;i+=d)
#define fol(i,l,r) for(register ll i=l,_lim_=r;i<=_lim_;i++)
#define fdl(i,r,l) for(register ll i=r,_lim_=l;i>=_lim_;i--)
#define fosl(i,l,r,d) for(register ll i=l,_lim_=r;i<=r;i+=d)
#define Clear(a) memset(a,0,sizeof(a))
#define Copy(a,b) memcpy(a,b,sizeof(b))
#define ALL(v) v.begin(),v.end()
#define SZ(v) ((int)v.size())

using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef long double ldb;
typedef double db;
typedef pair<int,ll> pi;

namespace io{
    const int L=(1<<21)+1;
    char ibuf[L],*iS,*iT,obuf[L],*oS=obuf,*oT=obuf+L-1,c,st[55];int f,tp;
    #ifdef whzzt
        #define gc() getchar()
    #else
        #define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,L,stdin),(iS==iT?EOF:*iS++)):*iS++)
    #endif
    inline void flush(){fwrite(obuf,1,oS-obuf,stdout);oS=obuf;}
    inline void putc(char x){*oS++=x;if(oS==oT)flush();}
    template
    inline void gi(I&x){
        for(f=1,c=gc();c<'0'||c>'9';c=gc())if(c=='-')f=-1;
        for(x=0;c<='9'&&c>='0';c=gc())x=x*10+(c&15);x*=f;
    }
    template
    inline void print(I x){
        if(!x)putc('0');if(x<0)putc('-'),x=-x;
        while(x)st[++tp]=x%10+'0',x/=10;
        while(tp)putc(st[tp--]);
    }
    inline void gs(char*s,int&l){
        for(c=gc();c<'a'||c>'z';c=gc());
        for(l=0;c<='z'&&c>='a';c=gc())s[l++]=c;
    }
    inline char O(){
        for(c=gc();c!='C'&&c!='Q'&&c!='R';c=gc());
        return c;
    }
    inline void ps(const char*s){
        fo(i,0,strlen(s)-1)putc(s[i]);
    }
};
using io::putc;
using io::gi;
using io::gs;
using io::print;

const int N=100005;
int n,x[N],cnt[1<<4],u,v,w,ans,dp[1<<16],st;
bool vi[1<<16];

int dfs(int S){
    if(!S)return 0;if(vi[S])return dp[S];
    vi[S]=true;
    int ret=233;
    fo(i,1,15)if(S>>i&1)fo(j,1,15)if(i!=j&&(S>>j&1)){
        int T=S^(1<1<1<<(i^j));
        if(__builtin_popcount(T)<__builtin_popcount(S))ret=min(ret,dfs(T)+1+(~T>>(i^j)&1));
    }
    return dp[S]=ret;
}

int main(){
    gi(n);
    fo(i,2,n)gi(u),gi(v),gi(w),x[u+1]^=w,x[v+1]^=w;
    fo(i,1,n)if(x[i])cnt[x[i]]++;
    fo(i,0,15)ans+=cnt[i]>>1,st|=(cnt[i]&1)<"%d\n",ans);
    return 0;
}

后面的题除了 H H 有一点思路之外全凉了,特别是 I I ,真的神仙。

G. Colorful Doors

2n 2 n 个位置,将 1...n 1... n 中的每个数放在两个位置。一开始在 1 1 位置,要走到 2n 2 n 位置,那么将每个数看做传送门,碰到放在的某个位置就传送到对应的另一个位置,从 1 1 开始走到 2n 2 n 结束。现在给定经过的所有的间隔,求一种放数字的方案。 n105 n ≤ 10 5

首先将 2n 2 n 1 1 也看做一个间隔,那么每个间隔有唯一前驱和后继,形成了若干环,然后我们把这个间隔的访问情况也当做一个 1 1 扔进序列里面就行了。

考虑如果输入的全是 1 1 的时候怎么做,稍微画一下可以发现 n n 是奇数的时候无解,偶数的时候只要 1,2,1,2,3,4,3,4... 1 , 2 , 1 , 2 , 3 , 4 , 3 , 4... 这样构造一下就行了,这一部分的问题很好解决。

同时我们会发现如果一开始有偶数个 1 1 ,补完之后变成了奇数个 1 1 ,也很好发现这样是无解的,因为有偶数个限制(单向边可以当成双向边用并查集并起来),每个点被偶数个限制作用,所以有用的限制也是偶数个,奇数个就无解了。

把间隔看成边,在中间建立点,那么一端为 0 0 的肯定是作为连接两段 1 1 出现的,只考虑两端都是 1 1 的这些点。

如果这些点的个数为奇数就是之前说的无解情况。如果是 4 4 的倍数直接按顺序连起来之后就变成了之前全是 1 1 n n 为偶数的情况。否则的话,假如只有一段的点只有两端,其他的点肯定连起来,就变成了访问的全是 1 1 n n 是奇数的情况了,无解。

如果有两段的话,我们考虑将这两段并起来。只要把第一段的第一个左右两边都是 1 1 的点和第二段中的第一个这样的点都赋值上同一个值,就可以将两条链交错拼起来,两端都是 1 1 的点的个数减少了 2 2 个,就可以使用之前的做法了。

这一部分题解里有图,可以稍微看一下。

如果还是不太懂可以看代码或者题解,虽然感觉题解也不太清楚。

神仙。

#include
#include
#include

#define SZ(v) ((int)v.size())
#define pb push_back
using namespace std;
typedef vector<int> VI;

const int N=200005;
VI li[N],id;
char s[N];
int n,m,L,ec,sum,mark[N];

void markit(){
    for(int i=0;i<<2for(int j:{0,1})for(int k:{0,1})mark[id[i<<2|j<<1|k]]=ec+k+1;
        ec+=2;
    }
}
void output(){
    puts("Yes");
    for(int i=0;iif(!mark[i])mark[i]=ec<0?-ec:++ec,ec=-ec;
        printf("%d ",mark[i]);
    }
    exit(0);
}

int main(){
    scanf("%d",&n);scanf("%s",s+1);s[0]='1';L=n<<1;
    for(int i=0,j;iif(s[i]=='0'&&s[(i+1)%L]=='1'){
        for(li[m].pb(i),j=(i+1)%L;s[j]=='1';j=(j+1)%L)li[m].pb(j);
        m++;
    }
    if(!m){
        if(~n&1){
            for(int i=0;ielse puts("No"),exit(0);
    }
    for(int i=0;i2;
    if(sum&1)puts("No"),exit(0);
    if(sum&2){
        for(int i=0;iif(SZ(li[i])>2)li[m-1].swap(li[i]);
        for(int i=0;i1;i++)if(SZ(li[i])>2){
            VI&a=li[i],&b=li[m-1];
            int la=SZ(a)-1,lb=SZ(b)-1,ea=a[la],eb=b[lb];
            mark[a[la-1]]=mark[b[lb-1]]=++ec;
            a.pop_back();b.pop_back();a.pop_back();b.pop_back();a.push_back(eb);b.push_back(ea);
            sum-=2;
            break;
        }
        if(sum&2)puts("No"),exit(0);
    }
    for(int i=0;i0]]=ec+1+i;
        mark[li[i][SZ(li[i])-1]]=ec+1+(i+1)%m;
        for(int j=1;j1;j++)id.pb(li[i][j]);
    }
    ec+=m;markit();output();
    return 0;
}

H. Generalized Insertion Sort

给定一棵 n2000 n ≤ 2000 个节点的树,每个点有点权,所有点的点权是一个排列,每次操作选择一个点 v v ,并将从根到 v v 的路径上的权值循环移位一次,其中根节点的点权变成节点 v v 的点权,节点 v v 的点权变成节点 v v 父亲的点权······。
要求在 25000 25000 次操作内将权值 i i 移动到节点 i i 上。

首先将树轻重链剖分,每次将最下层的重链拿走,这样由于轻重链剖分的性质,最多会拿 O(logn) O ( log ⁡ n ) 次,实际上树链剖分最多有 10 10 层。
考虑链剖之后如何将最下层的所有点上的权值修改正确,不妨称这些最下层重链上的点为红点,其他点为蓝点。
首先如果是一条链的话很好处理,第 i i 次操作维护链的最后 i i 个的偏序正确即可。这样我们只要 n n 次操作。考虑如何拓展到树上。
注意到我们每次只有均摊的 2500 2500 次操作,和 n n 是同阶的,所以我们在每次要尽量使得每个权值只出现在根节点一次,实际上我们之前的链的做法也就是保证了这一点。
B B 为已访问的蓝点集合, R R 为未到过根的权值集合。
定义势能函数 δ=|R|+iB[Rsubtree{i}] δ = | R | + ∑ i ∈ B [ R ∩ s u b t r e e { i } ≠ ∅ ] 。下面我们考虑 δ δ 函数的变化,显然终止状态的 δ0 δ ≥ 0
假设有一个红点到了根,这个红点一定未访问,而扔下去后前面的值一定减小 1 1 ,后面的值有 |red| | r e d | 次会增加,对于整个过程来说是 n n 次。
假设有一个蓝点到了根,那么我们肯定是将这个蓝点扔到某个蓝点堆的上方。那么我们扔到哪一个呢?我们扔到一个蓝点堆的堆顶上方,使得整个子树的权值都不在 R R 中,这样的方案显然存在,而这也不会对势能函数的后半部分产生影响。而一个已经被访问过的蓝点重新到根意味着后面的值要 1 − 1 ,于是最多有 |red| | r e d | 个蓝点被扔了两次。所以这部分总共会有 n n 个蓝点被多扔。
所以总的时间复杂度为 O(n2logn) O ( n 2 log ⁡ n ) ,操作的复杂度为 O(nlogn) O ( n log ⁡ n )

#include

#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-funroll-loops")
#pragma GCC target("avx,sse4")

#define mms(a,n) memset(a,0,sizeof((a)[0])*(n))
#define mmp(a,b,n) memcpy(a,b,sizeof((b)[0])*(n))
#define lowbit(x) ((x)&-(x))
#define pb push_back
#define fi first
#define se second
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define fo(i,l,r) for(register int i=l,_lim_=r;i<=_lim_;i++)
#define fd(i,r,l) for(register int i=r,_lim_=l;i>=_lim_;i--)
#define fos(i,l,r,d) for(register int i=l,_lim_=r;i<=r;i+=d)
#define fol(i,l,r) for(register ll i=l,_lim_=r;i<=_lim_;i++)
#define fdl(i,r,l) for(register ll i=r,_lim_=l;i>=_lim_;i--)
#define fosl(i,l,r,d) for(register ll i=l,_lim_=r;i<=r;i+=d)
#define Clear(a) memset(a,0,sizeof(a))
#define Copy(a,b) memcpy(a,b,sizeof(b))
#define ALL(v) v.begin(),v.end()
#define SZ(v) ((int)v.size())

using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef long double ldb;
typedef double db;
typedef pair<int,ll> pi;

namespace io{
    const int L=(1<<21)+1;
    char ibuf[L],*iS,*iT,obuf[L],*oS=obuf,*oT=obuf+L-1,c,st[55];int f,tp;
    #ifdef whzzt
        #define gc() getchar()
    #else
        #define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,L,stdin),(iS==iT?EOF:*iS++)):*iS++)
    #endif
    inline void flush(){fwrite(obuf,1,oS-obuf,stdout);oS=obuf;}
    inline void putc(char x){*oS++=x;if(oS==oT)flush();}
    template<class I>
    inline void gi(I&x){
        for(f=1,c=gc();c<'0'||c>'9';c=gc())if(c=='-')f=-1;
        for(x=0;c<='9'&&c>='0';c=gc())x=x*10+(c&15);x*=f;
    }
    template<class I>
    inline void print(I x){
        if(!x)putc('0');if(x<0)putc('-'),x=-x;
        while(x)st[++tp]=x%10+'0',x/=10;
        while(tp)putc(st[tp--]);
    }
    inline void gs(char*s,int&l){
        for(c=gc();c<'a'||c>'z';c=gc());
        for(l=0;c<='z'&&c>='a';c=gc())s[l++]=c;
    }
    inline char O(){
        for(c=gc();c!='C'&&c!='Q'&&c!='R';c=gc());
        return c;
    }
    inline void ps(const char*s){
        fo(i,0,strlen(s)-1)putc(s[i]);
    }
};
using io::putc;
using io::gi;
using io::gs;
using io::print;

const int N=2005;
int n,p[N],w[N],sz[N],son[N],bel[N],bot[N],m,pos[N];
bool del[N],red[N],vi[N];
vector<int>e[N],O,li[N];

void dfs(int u){
    sz[u]=1;
    for(int v:e[u]){
        dfs(v),sz[u]+=sz[v];
        if(sz[v]>sz[son[u]])son[u]=v;
    }
}
void dfs(int u,int b){
    bel[u]=b;li[b].pb(u);pos[u]=SZ(li[b]);
    if(u==b)red[u]=true;if(e[u].size()>1)red[b]=false;
    if(son[u])dfs(son[u],b);else bot[b]=u;
    for(int v:e[u])if(v!=son[u])dfs(v,v);
    if(red[u])for(int v:li[u])red[v]=true,m++;
}
void flip(int u){
    O.pb(u);for(int v=p[u];v;v=p[v])swap(w[v],w[u]);
}
int find(int u){
    bool o=true;
    for(int v:e[u]){
        int p=find(v);
        if(p>0)return p;
        o&=p==-1; 
    }
    if(o)return vi[w[u]]?-1:u;
    return 0;
}
void move(){
    int u=w[1];if(!vi[u])vi[u]=true;m-=red[u];
    if(red[u])for(int v=bot[bel[u]];v;v=p[v])if(!red[w[v]]||!vi[w[v]]||pos[w[v]]<=pos[u])return flip(v);
    flip(find(1));
}
bool fix(){
    fo(i,2,n)if(!del[i])e[p[i]].pb(i);
    if(!e[1].size())return false;
    dfs(1);dfs(1,1);
    while(m)move();
    fo(i,1,n)e[i].clear(),li[i].clear(),del[i]|=red[i],red[i]=vi[i]=bot[i]=pos[i]=bel[i]=son[i]=sz[i]=0;m=0;
    return true;
}

int main(){
    gi(n);
    fo(i,2,n)gi(p[i]),p[i]++;
    fo(i,1,n)gi(w[i]),w[i]++;
    while(fix());
    print(O.size());putc('\n');
    for(int v:O)print(v-1),putc('\n');
    io::flush();
    return 0;
}

I. Simple APSP Problem

n×m (n,m106) n × m   ( n , m ≤ 10 6 ) 的网格上有 K30 K ≤ 30 个障碍,求所有点之间两两的最短路之和。

神仙题。考虑两行如果都是空格,那么我们可以计算出中间的边经过的次数然后合并。同时如果两列都是空格,也可以类似处理,于是这些没有障碍的行列之间的边权都变成了 0 0 。我们只要将 0 0 联通块缩起来,这些点出去的最短路是相同的,问题就只要直接进行一次 BFS B F S 即可。时间复杂度 O(K4) O ( K 4 )

#include

#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-funroll-loops")
#pragma GCC target("avx,sse4")

#define mms(a,n) memset(a,0,sizeof((a)[0])*(n))
#define mmp(a,b,n) memcpy(a,b,sizeof((b)[0])*(n))
#define lowbit(x) ((x)&-(x))
#define pb push_back
#define fi first
#define se second
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define fo(i,l,r) for(register int i=l,_lim_=r;i<=_lim_;i++)
#define fd(i,r,l) for(register int i=r,_lim_=l;i>=_lim_;i--)
#define fos(i,l,r,d) for(register int i=l,_lim_=r;i<=r;i+=d)
#define fol(i,l,r) for(register ll i=l,_lim_=r;i<=_lim_;i++)
#define fdl(i,r,l) for(register ll i=r,_lim_=l;i>=_lim_;i--)
#define fosl(i,l,r,d) for(register ll i=l,_lim_=r;i<=r;i+=d)
#define Clear(a) memset(a,0,sizeof(a))
#define Copy(a,b) memcpy(a,b,sizeof(b))
#define ALL(v) v.begin(),v.end()
#define SZ(v) ((int)v.size())

using namespace std;
typedef unsigned long long ull;
typedef long long ll;
typedef long double ldb;
typedef double db;
typedef pair<int,int> pi;
template<class T>inline void vnique(vector &r){sort(ALL(r));auto t=unique(ALL(r));r.erase(t,r.end());}

const int N=65,M=1000005,P=1e9+7,dx[]={-1,1,0,0},dy[]={0,0,-1,1};

int n,m,K,a,x[N],y[N],lx,ly,cnt[N][N],s1[M],s2[M],dis[N][N],ql,qr;
bool fb[N][N],mark1[M],mark2[M];
vector<int>px,py;
pi qu[N*N];

void bfs(int x,int y){
    fo(i,0,lx)fo(j,0,ly)dis[i][j]=N<<1;
    dis[x][y]=0;qu[ql=qr=1]={x,y};
    while(ql<=qr){
        int u=qu[ql].fi,v=qu[ql].se;ql++;
        fo(i,0,3){
            int tu=u+dx[i],tv=v+dy[i];
            if(tu>lx||tv>ly||tu<0||tv<0||dis[tu][tv]<=dis[u][v]+1||fb[tu][tv])continue;
            dis[tu][tv]=dis[u][v]+1;qu[++qr]={tu,tv};
        }
    }
}

int main(){
    scanf("%d%d%d",&n,&m,&K);
    px.pb(1);px.pb(n+1);py.pb(1);py.pb(m+1); 
    fo(i,1,n)s1[i]=m;fo(i,1,m)s2[i]=n;
    fo(i,1,K){
        scanf("%d%d",&x[i],&y[i]);
        x[i]++,px.pb(x[i]),px.pb(x[i]+1);
        y[i]++,py.pb(y[i]),py.pb(y[i]+1);
        s1[x[i]]--;mark1[x[i]]=true;
        s2[y[i]]--;mark2[y[i]]=true;
    }
    fo(i,1,n)(s1[i]+=s1[i-1])%=P;fo(i,1,m)(s2[i]+=s2[i-1])%=P;
    fo(i,1,n-1)if(!mark1[i]&&!mark1[i+1])a=(a+(ll)2*s1[i]*(s1[n]-s1[i]))%P;
    fo(i,1,m-1)if(!mark2[i]&&!mark2[i+1])a=(a+(ll)2*s2[i]*(s2[m]-s2[i]))%P;
    vnique(px);vnique(py);lx=SZ(px)-2;ly=SZ(py)-2;
    fo(i,1,K)fb[lower_bound(ALL(px),x[i])-px.begin()][lower_bound(ALL(py),y[i])-py.begin()]=true;
    fo(i,0,lx)fo(j,0,ly)cnt[i][j]=(ll)(px[i+1]-px[i])*(py[j+1]-py[j])%P;
    fo(i,0,lx)fo(j,0,ly)if(!fb[i][j]){
        bfs(i,j);
        fo(k,0,lx)fo(t,0,ly)if(!fb[k][t])a=(a+(ll)cnt[i][j]*dis[k][t]%P*cnt[k][t])%P;
    }
    if(a<0)a+=P;a=(ll)a*(P/2+1)%P;
    printf("%d\n",a);
    return 0;
}

J.

现在有一个由若干 1×1×1 1 × 1 × 1 的小立方体组成的大小为 A×B×C A × B × C 的空间(坐标从 0 0 开始标号, A,B,C100 A , B , C ≤ 100 )。现在要将空间分成若干个大小为 a×b×c a × b × c 的环面立方体,其中以 (p,q,r) ( p , q , r ) 为左下角的环面立方体由所有坐标为 (p+imodA,q+jmodB,r+kmodC) ( p + i mod A , q + j mod B , r + k mod C ) 的格子组成,其中 0i<a,0j<b,0k<c 0 ≤ i < a , 0 ≤ j < b , 0 ≤ k < c 。求划分的方案数。

首先考虑 2D 2 D 的情形,答案很好计算,因为一定有行划分或列划分,只要容斥即可。

3D 3 D 的时候有 trival t r i v a l 的情况是存在一维的划分,这种情况只要类似 2D 2 D 一样计算即可。
当情况变得不 trival t r i v a l 时,我们可以将一维投影,就是说在 (p+imodA,q+jmodB,r+kmodC) ( p + i mod A , q + j mod B , r + k mod C ) 上标上 k k 后,计算每层的方案数。实际上我们确定了 z=0 z = 0 平面的样子就可以确定整个图形的样子了。而这里有个神仙的事情就是不 trival t r i v a l 的图形的每一层(表示成数字)必然存在行列划分,只有一种划分就可以直接割开,而行列划分就是可以将图剖分成网格一样的小长方形的那种。我们将这个基础的行列划分的一些行或列 shift s h i f t 若干次, 条件实际上就是对于所有的 z z 至少有行的 shift s h i f t 也有列的 shift s h i f t ,于是只要容斥即可。
最后利用 f[p][q] f [ p ] [ q ] 表示有 p p 0 0 q q 1 1 的不 trival t r i v a l 的图案个数, dp d p 即可。

可以看 w×h w × h 的博客和题解,神仙 w×h w × h

估计也没人能看懂。

#include

#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-funroll-loops")
#pragma GCC target("avx,sse4")

#define mms(a,n) memset(a,0,sizeof((a)[0])*(n))
#define mmp(a,b,n) memcpy(a,b,sizeof((b)[0])*(n))
#define lowbit(x) ((x)&-(x))
#define pb push_back
#define fi first
#define se second
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define fo(i,l,r) for(register int i=l,_lim_=r;i<=_lim_;i++)
#define fd(i,r,l) for(register int i=r,_lim_=l;i>=_lim_;i--)
#define fos(i,l,r,d) for(register int i=l,_lim_=r;i<=r;i+=d)
#define fol(i,l,r) for(register ll i=l,_lim_=r;i<=_lim_;i++)
#define fdl(i,r,l) for(register ll i=r,_lim_=l;i>=_lim_;i--)
#define fosl(i,l,r,d) for(register ll i=l,_lim_=r;i<=r;i+=d)
#define Clear(a) memset(a,0,sizeof(a))
#define Copy(a,b) memcpy(a,b,sizeof(b))
#define ALL(v) v.begin(),v.end()
#define SZ(v) ((int)v.size())
#define sqr(x) ((x)*(x))

using namespace std;
typedef unsigned int uint;
typedef unsigned long long ull;
typedef long long ll;
typedef long double ldb;
typedef double db;
typedef pair<int,int> pi;
typedef vector<int> VI;
typedef vector VII;

namespace Main{

const int N=105,P=1e9+7;

inline int fpow(int a,int t){
    static int r;
    for(r=1;t;t>>=1,a=(ll)a*a%P)if(t&1)r=(ll)r*a%P;
    return r;
}

int f[N][N],v[N][N];
int Solve1D(int a,int A){return a;}
int Solve2D(int a,int A,int b,int B){
    return (((ll)a*fpow(Solve1D(b,B),A/a)+(ll)b*fpow(Solve1D(a,A),B/b)-(ll)a*b)%P+P)%P;
}
int Solve3D(int a,int A,int b,int B,int c,int C){
    if(A%a||B%b||C%c)return 0;
    // trival cases
    int ae=A/a,be=B/b,ce=C/c;
    int ret=(
    ((ll)a*fpow(Solve2D(b,B,c,C),ae)
    +(ll)b*fpow(Solve2D(a,A,c,C),be)
    +(ll)c*fpow(Solve2D(a,A,b,B),ce)
    -(ll)a*b*fpow(Solve1D(c,C),ae*be)
    -(ll)a*c*fpow(Solve1D(b,B),ae*ce)
    -(ll)b*c*fpow(Solve1D(a,A),be*ce)
    +(ll)a*b*c)%P+P)%P;
    // non-trival cases
    fo(i,**v=1,max(ae,be))fo(j,*v[i]=1,i)v[i][j]=(v[i-1][j-1]+v[i-1][j])%P;
    fd(i,ae,1)fd(j,be,1){
        f[i][j]=(ll)v[ae][i]*v[be][j]%P*fpow(c,(ae-i)*(be-j))%P;
        fo(k,i,ae)fo(t,j,be)if(k>i||t>j)f[i][j]=(f[i][j]-(ll)f[k][t]*v[k][i]%P*v[t][j]%P+P)%P;
        int p=fpow(b,i),q=fpow(a,j),w=((fpow(p+q-1,ce)-fpow(p,ce)-fpow(q,ce)+1)%P+P)%P;
        if(ireturn ret;
}

}

int a,b,c,A,B,C;

int main(){
    cin>>a>>b>>c>>A>>B>>C;
    cout<return 0;
}

你可能感兴趣的:(图论,-,树,图论,-,图,搜索,构造,动态规划)