0%

20901202-AGC025

D

给定 $n,D_1,D_2$ ,要求构造一个在 $2n\times 2n$ 的网格中选出 $n^2$ 个点的方案,是任意两点间距离不为 $\sqrt{D_1}$ 或 $\sqrt{D_2}$

由题有 $|X|=4\times N^2$ ,要求 $|S|=N^2$ ,即,需要找到 $\frac{1}{4}$ 的点

容易想到当需要 $\frac{1}{2}$ 的点时,(即只有一个禁止距离时,$|S|=2\times N^2$) ,此时用二分图恰好达到目的

即:对于 $A,B\in X$ ,若 $|AB|=\sqrt{D}$ 则连接 $AB$ ,最后得到图 $G$ ,要求 $G$ 独立集且包含至少一半的节点,显然这是二分图

考虑图 $G$ 是否为二分图(考虑到欧几里得距离涉及到平方和,从数论角度):

设 $|AB|^2=4^k\times D$ ,只考虑 $\mod 2^k$:

  • $D\equiv 1\pmod{2}$

    此时 $|AB|^2\equiv (A_x+A_y)+(B_x+B_y)\pmod{2}$ ,则 $A_x+A_y\not\equiv B_x+B_y\pmod{2}$ ,则,按照 $x+y$ 的奇偶性分为两组,有边相连的二点必定在不同组中,满足二分图

  • $D\equiv 2\pmod{4}$

    同上易得 $A_x-B_x\equiv 1\pmod{2} \wedge A_y-B_y\equiv \pmod{2}$ ,即 $A_x\not\equiv B_x\pmod{2}$ ,则,按照 $x$ 的奇偶性分组也满足二分图

  • $D\equiv 0\pmod{4}$

    同时易得 $A_x\equiv B_x\pmod{2}\wedge A_y\equiv B_y\pmod{2}$ ,此时按照 $(x\mod 2,y\mod 2)$ 分为四个等价类,有边相连的点一定在同一个里面,对一个等价类,缩放为 $\frac{x}{2},\frac{y}{2},\frac{D}{4}$ ,归纳结论成立

再考虑两个距离:自由组合即可,即,独立考虑两种颜色,进行不同染色方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const int N=610;
int n,D1,D2,d;
int a[N][N],col[4];

inline void color(int dist,int bit){
rei len=ctz(dist)/2; dist>>=2*len;
if((dist&3)==1) for(rei i=0;i<n;++i) for(rei j=0;j<n;++j) a[i][j]|=((i^j)>>len&1)<<bit;
else if((dist&3)==2) for(rei i=0;i<n;++i) for(rei j=0;j<n;++j) a[i][j]|=(i>>len&1)<<bit;
}

int main(){
scanf("%d%d%d",&n,&D1,&D2); d=n*n,n*=2;
color(D1,0),color(D2,1);
for(rei i=0;i<n;++i) for(rei j=0;j<n;++j) ++col[ a[i][j] ];
rei c=max_element(col,col+4)-col;
for(rei i=0;i<n && d;++i) for(rei j=0;j<n && d;++j) if(a[i][j]==c) --d,printf("%d %d\n",i,j);
getchar(),getchar();
return 0;
}

E

给出 $n$ 节点的树和 $m$ 条树上路径,为每一路径定向,第 $i$ 条边 $(a_i,b_i)$ 的权值为:同时被某两条路径沿 $a_i\rightarrow b_i,b_i\rightarrow a_i$ 经过的条数,求最大权值和及定向方案

这是一个阴间构造题:

设经过每条边的路径数 $c_e$ ,则答案上界 $\sum_{\min\{2,c_e\}}$

归纳法证明:

  • $n=1$ 显然成立
  • $n>1$ ,设与叶子 $v$ 相连的边 $e$ ,点为 $w$
    • $c_e=0$ 直接删去 $v$
    • $c_e=1$ 将路径 $(v,x)$ 替换为 $(w,x)$
    • $c_e>1$ 任意选择两条路径 $(v,a),(v,b)$ ,设两路径的交 $(v,c)$ ,那么 $v\rightarrow c$ 的所有边贡献都为 $2$ ,剩下的路径等价表示为 $(a,b)$

执行到最后一个点即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
const int N=2e3+100;
int n,m;
int A[N],B[N],ans,a[N<<1],b[N<<1];
int q[N],anc[N],deg[N],dep[N];
int dir[N<<1];
int ch[N<<1][2];
bool rev[N<<1],vis[N],mark[N<<1],used[N][2];
int head[N];
struct edge{ int ver,Next; edge(int ver=0,int Next=0):ver(ver),Next(Next){}};
vector<edge> G;

inline void addedge(int u,int v){ G.push_back(edge(v,head[u])),head[u]=G.size()-1; G.push_back(edge(u,head[v])),head[v]=G.size()-1; ++deg[u],++deg[v];}

void dfs(int u,int c){
if(!u) return;
c^=rev[u]; dir[u]=c;
dfs(ch[u][0],c);
dfs(ch[u][1],c^1);
}

void getdep(int u){
for(rei i=head[u];~i;i=G[i].Next){
rei v=G[i].ver; if(v==anc[u]) continue;
dep[v]=dep[u]+1,anc[v]=u;
getdep(v);
}
}

inline void upd(int x,int c){ used[x][c] ? 0 : (used[x][c]=1,++ans);}

int main(){
memset(head,-1,sizeof head);
scanf("%d%d",&n,&m);
for(rei i=1,a,b;i<n;++i) scanf("%d%d",&a,&b),addedge(a,b);
for(rei i=1;i<=m;++i) scanf("%d%d",&A[i],&B[i]),a[i]=A[i],b[i]=B[i];
rei hd=0,tl=0;
for(rei i=1;i<=n;++i) if(deg[i]==1) q[tl++]=i;
rei T=n,cnt=m;
while(T--){
rei u=q[hd++]; vis[u]=1;
for(rei i=head[u],y;~i;i=G[i].Next){ y=G[i].ver; if(--deg[y]==1) q[tl++]=y;}
vector<int> E;
for(rei i=1;i<=cnt;++i) if(!mark[i]){
if(a[i]==u && b[i]==u){ dfs(i,0); continue;}
if(b[i]==u) swap(a[i],b[i]),rev[i]^=1;
if(a[i]==u) E.push_back(i);
}
rei w=-1;
for(rei i=head[u],y;~i;i=G[i].Next){
y=G[i].ver; if(vis[y]) continue;
w=y; break;
}
while(E.size()){
if(E.size()==1){ rei e=E[0]; a[e]=w; break;}
rei x=E.back(); E.pop_back();
rei y=E.back(); E.pop_back();
++cnt; ch[cnt][0]=x,ch[cnt][1]=y;
a[cnt]=b[y],b[cnt]=b[x];
mark[x]=mark[y]=1;
}
}
getdep(1);
for(rei i=1;i<=m;++i){
if(dir[i]) swap(A[i],B[i]);
rei u=A[i],v=B[i];
while(dep[u]>dep[v]) upd(u,0),u=anc[u];
while(dep[u]<dep[v]) upd(v,1),v=anc[v];
while(u!=v){
upd(u,0),u=anc[u];
upd(v,1),v=anc[v];
}
}
printf("%d\n",ans);
for(rei i=1;i<=m;++i) printf("%d %d\n",A[i],B[i]);
getchar(),getchar();
return 0;
}

F

给出长度为 $n$ 的二进制数 $x$ 和长度 $m$ 的 $y$ ,进行 $k$ 次操作:设 $z=x\And y,x+=z,y+=z$ ,求出操作后的 $x,y$

暴力模拟啊嗯

从高位到低位每位重复模拟 $k$ 遍处理:

考虑 $(1,1)$ 进位时:

遇到两个数同一位不相同的情况 $(0,1),(1,0)$ 则暴力进位

而遇到连续的一段 $(0,0)$ 可以直接移过去,

栈维护下标从大到小(当前位置)的 $(0,1),(1,0)$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
const int N=2e6+100;
struct node{
int id,par;
}sta[N],tmp[N];
int n,m,k,top,cnt,ns[N],nt[N],as[N],at[N];
char s[N],t[N];

int main(){
scanf("%d%d%d",&n,&m,&k);
scanf("%s",s+1);scanf("%s",t+1);
reverse(s+1,s+n+1);reverse(t+1,t+m+1);
for(rei i=1;i<=n;++i) ns[i]=s[i]-'0';
for(rei i=1;i<=m;++i) nt[i]=t[i]-'0';
rei T=max(n,m);
for(rei i=T;i;--i){
if(ns[i]==1 && nt[i]==1){
rei op=k,pos=i,x=3;cnt=0;
while(top){
if(x==3){
if(op>=sta[top].id-pos){
op-=sta[top].id-pos; pos=sta[top].id;
tmp[++cnt]=(node){ pos,x^sta[top].par};
x=x&sta[top].par;
}
else break;
}
else if(x!=0){
if(sta[top].id==pos+1){
pos=sta[top].id;
x^sta[top].par ? x=3 : x=x&sta[top].par;
}
else break;
}
else break;
--top;
}
if(x!=0 && x!=3){ ++pos; tmp[++cnt]=(node){pos,x};}
else if(x){ pos+=op; as[pos]=at[pos]=1;}
for(rei j=cnt;j;--j) sta[++top]=tmp[j];
}
else if(ns[i] || nt[i]) sta[++top]=(node){ i,ns[i]<<1|nt[i]};
}
for(rei i=1;i<=top;++i) as[ sta[i].id ]=(sta[i].par>>1)&1,at[ sta[i].id ]=sta[i].par&1;
rei c=T+k; while(!as[c]) --c; for(rei i=c;i>=1;--i) printf("%d",as[i]); puts("");
c=T+k; while(at[c]==0) --c; for(rei i=c;i;--i) printf("%d",at[i]); puts("");
getchar(),getchar();
return 0;
}