Реши задачу(и) с контеста на Python. Требования к...
Tạo vào: 10 tháng 10, 2026
Trả lời bằng GPT-5.6 Thinking bởi Chat01
Tạo vào: 10 tháng 10, 2026
Trả lời bằng GPT-5.6 Thinking bởi Chat01
Реши задачу(и) с контеста на Python. Требования к стилю кода:
pythonfrom functools import lru_cache from itertools import combinations def game(n, edges, x, y): adj=[[] for _ in range(n)] for a,b in edges: adj[a].append(b); adj[b].append(a) init=(1<<x)|(1<<y) @lru_cache(None) def f(mask): if mask==(1<<n)-1:return 0 best=-99 for v in range(n): if not(mask>>v&1): c=sum(mask>>u&1 for u in adj[v]) r=int(c==1) best=max(best,r-f(mask|1<<v)) return best return f(init) def path(n,x,y): return game(n,[(i,i+1) for i in range(n-1)],x,y) for n in range(2,9): print('n',n, [[path(n,x,y) for y in range(x+1,n)] for x in range(n-1)])
pythonimport random, itertools, collections, math def randtree(n): p=[random.randrange(n) for _ in range(n-2)] deg=[1]*n for z in p:deg[z]+=1 import heapq h=[i for i,d in enumerate(deg) if d==1];heapq.heapify(h) e=[] for z in p: a=heapq.heappop(h); e.append((a,z)); deg[a]-=1;deg[z]-=1 if deg[z]==1: heapq.heappush(h,z) a=heapq.heappop(h);b=heapq.heappop(h);e.append((a,b)) return e for n in range(2,10): vals=set() ex={} for _ in range(200): e=randtree(n) x,y=random.sample(range(n),2) g=game(n,e,x,y);vals.add(g);ex[g
pythondef state_vals(n,e): adj=[[] for _ in range(n)] for a,b in e:adj[a].append(b);adj[b].append(a) @lru_cache(None) def f(mask): if mask==(1<<n)-1:return 0 return max((int(sum(mask>>u&1 for u in adj[v])==1)-f(mask|1<<v) for v in range(n) if not(mask>>v&1)), default=0) d={} for mask in range(1<<n): d[mask]=f(mask) return d for n in range(2,8): e=randtree(n) vals=set(state_vals(n,e).values()) print(n,e,vals)
pythondef dist_info(n,e,x,y): adj=[[] for _ in range(n)] for a,b in e:adj[a].append(b);adj[b].append(a) par=[-1]*n; q=collections.deque([x]); par[x]=x while q: v=q.popleft() if v==y:break for u in adj[v]: if par[u]<0:par[u]=v;q.append(u) path=[];v=y while v!=x:path.append(v);v=par[v] path.append(x);path=path[::-1] return len(path)-1, tuple(len(adj[v]) for v in path), path, adj for n in range(4,8): groups=collections.defaultdict(set) for _ in range(1000): e=randtree(n); x,y=random.sample(range(n),2) d,degs,pa,adj=dist_info(n,e,x,y) groups[d].add(game(n,e,x,y)) print("n",n,{d:s for d,s in groups.items()})
pythonfor n in [4,5,6]: seen={} for _ in range(5000): e=randtree(n);x,y=random.sample(range(n),2) d,degs,pa,adj=dist_info(n,e,x,y) if d==2: g=game(n,e,x,y) if g not in seen:seen[g]=(e,x,y,pa,degs) if len(seen)==2:break print("n",n,seen)
pythondef analyze_state(n,e): adj=[[] for _ in range(n)] for a,b in e:adj[a].append(b);adj[b].append(a) vals=state_vals(n,e) grp=collections.defaultdict(set) for mask,v in vals.items(): z=[i for i in range(n) if mask>>i&1] if not z: c=0 else: vis=set();c=0 for s in z: if s in vis:continue c+=1;stack=[s];vis.add(s) while stack: a=stack.pop() for b in adj[a]: if mask>>b&1 and b not in vis:vis.add(b);stack.append(b) m=n-len(z) # boundary scoring count b=sum(1 for x in range(n) if not(mask>>x&1) and sum(mask>>u&1 for u in adj[x])==1) grp[(m%2,c,b)].add(v) return grp e=[(0,3),(1,2),(2,3),(3,4),(4,5)] grp=analyze_state(6,e) [(k,v) for k,v in grp.items() if len(v)>1][:20], len(grp) ([((0, 2, 2), {0, 1}), ((1, 2, 1), {0, 1}), ((1, 2, 2), {0, 1})], 21)
pythondef local_game_tree(n,e,boundaries): # vertices 0..n-1 unselected, add external selected leaves attached to specified vertices counts maybe # boundaries list vertices each ext edge N=n+len(boundaries) ee=e[:] for idx,v in enumerate(boundaries): ee.append((v,n+idx)) adj=[[] for _ in range(N)] for a,b in ee:adj[a].append(b);adj[b].append(a) init=0 for i in range(n,N):init|=1<<i @lru_cache(None) def f(mask): if all(mask>>i&1 for i in range(n)): return 0 return max(int(sum(mask>>u&1 for u in adj[v])==1)-f(mask|1<<v) for v in range(n) if not(mask>>v&1)) return f(init) for sz in range(1,8): vals1=set(); vals2=set() for _ in range(200): e=randtree(sz) if sz>1 else [] vals1.add(local_game_tree(sz,e,[random.randrange(sz)])) if sz>=1: a,b=random.randrange(sz),random.randrange(sz) # two boundary edges, could same vertex vals2.add(local_game_tree(sz,e,[a,b])) print(sz,vals1, vals2)
pythondef path_weights(n,e,x,y): d,degs,p,adj=dist_info(n,e,x,y) ps=set(p) w=[] for v in p: c=0 for u in adj[v]: if u in ps: continue st=[(u,v)] while st: a,pa=st.pop();c+=1 for b in adj[a]: if b!=pa:st.append((b,a)) w.append(c) return tuple(w) for n in range(4,10): groups=collections.defaultdict(set) for _ in range(5000): e=randtree(n);x,y=random.sample(range(n),2) key=(dist_info(n,e,x,y)[0],path_weights(n,e,x,y)) groups[key].add(game(n,e,x,y)) bad=[(
pythonfor n in range(4,11): groups=collections.defaultdict(set) for _ in range(10000): e=randtree(n);x,y=random.sample(range(n),2) w=path_weights(n,e,x,y) key=(len(w), tuple(z%2 for z in w), n%2) groups[key].add(game(n,e,x,y)) bad=[(k,v) for k,v in groups.items() if len(v)>1] print(n,"groups",len(groups),"bad",len(bad),bad[:2])
pythondef cater(w): # path vertices 0..L-1, x=0,y=L-1; attach w_i leaves L=len(w); e=[(i,i+1) for i in range(L-1)] cur=L for i,a in enumerate(w): for _ in range(a): e.append((i,cur));cur+=1 return cur,e,0,L-1 for L in range(2,7): print("L",L) for bits in itertools.product([0,1], repeat=L): n,e,x,y=cater(bits) g=game(n,e,x,y) print(''.join(map(str,bits)),g,end='; ') print()
pythondef optimal_moves(n,e,x,y): adj=[[] for _ in range(n)] for a,b in e:adj[a].append(b);adj[b].append(a) init=(1<<x)|(1<<y) @lru_cache(None) def f(mask): if mask==(1<<n)-1:return 0 return max(int(sum(mask>>u&1 for u in adj[v])==1)-f(mask|1<<v) for v in range(n) if not(mask>>v&1)) val=f(init); opts=[] for v in range(n): if not(init>>v&1): r=int(sum(init>>u&1 for u in adj[v])==1) z=r-f(init|1<<v) if z==val:opts.append((v,r,f(init|1<<v))) return val,opts found=0 for n in range(3,10): for _ in range(3000): e=randtree(n);x,y=random.sample(range(n),2) val
pythonfound=0 for n in range(4,11): for _ in range(5000): e=randtree(n);x,y=random.sample(range(n),2) val,op=optimal_moves(n,e,x,y) adj=[[] for _ in range(n)] for a,b in e: adj[a].append(b);adj[b].append(a) init=(1<<x)|(1<<y) has1=any(sum(init>>u&1 for u in adj[v])==1 for v in range(n) if not(init>>v&1)) if has1 and op and all(r==0 for _,r,_ in op): print("only zero",n,val,e,x,y,op) found=1;break if found:break
pythondef check_property(n,e): adj=[[] for _ in range(n)] for a,b in e:adj[a].append(b);adj[b].append(a) @lru_cache(None) def f(mask): if mask==(1<<n)-1:return 0 return max(int(sum(mask>>u&1 for u in adj[v])==1)-f(mask|1<<v) for v in range(n) if not(mask>>v&1)) for mask in range(1<<n): if mask==(1<<n)-1:continue sc=[] for v in range(n): if not mask>>v&1: r=int(sum(mask>>u&1 for u in adj[v])==1) if r:sc.append(v) if sc: val=f(mask) if all(1-f(mask|1<<v)<val for v in sc): return mask,val,sc return None
pythonfor n in range(2,8): e=randtree(n); d=state_vals(n,e) grp={0:set(),1:set()} for mask,v in d.items(): m=n-mask.bit_count() grp[m%2].add(v) print(n,e,grp)
pythonfor n in range(2,9): bads=[] for _ in range(50): e=randtree(n); d=state_vals(n,e) for mask,v in d.items(): if mask and v==-1: bads.append((e,mask)) break if bads:break print(n, bool(bads), bads[:1])
pythondef compare_normal(n,e): adj=[[] for _ in range(n)] for a,b in e:adj[a].append(b);adj[b].append(a) @lru_cache(None) def f(mask): if mask==(1<<n)-1:return 0 return max(int(sum(mask>>u&1 for u in adj[v])==1)-f(mask|1<<v) for v in range(n) if not(mask>>v&1)) @lru_cache(None) def g(mask): for v in range(n): if not mask>>v&1 and sum(mask>>u&1 for u in adj[v])==1: if g(mask|1<<v)==0:return 1 return 0 for mask in range(1,1<<n): if f(mask)!=g(mask): return mask,f(mask),g(mask) return None for n in range(2,9): bad=None for _ in range(100): e=randtree(n)
pythondef formula(n,e,x,y): adj=[[] for _ in range(n)] for a,b in e: adj[a].append(b);adj[b].append(a) par=[-1]*n q=collections.deque([x]);par[x]=x while q: v=q.popleft() if v==y:break for u in adj[v]: if par[u]<0: par[u]=v;q.append(u) p=[] v=y while v!=x: p.append(v);v=par[v] p.append(x);p.reverse() on=[False]*n for v in p:on[v]=True b=[0]*len(p) # Count each off-path component size via DFS from neighbors for i,v in enumerate(p): for u in adj[v]: if on[u]:continue st=[(u,v)] while st: a,pa=st.pop(); b[i]^=1 # only parity for z in adj[a]: if z!=pa:st.append((z,a)) if len(p)==2: return b[0]^b[1] # SG 0/1 nonzero k=len(p)-2 dp=[0]*k for ln in range(2,k+1): nd=[0]*(k-ln+1) for ii in range(k-ln+1): l=ii+1 # p index r=l+ln-1 a=dp[ii+1] ^ b[l] c=dp[ii] ^ b[r] if a!=0 and c!=0: z=0 elif a!=1 and c!=1: z=1 else: z=2 nd[ii]=z dp=nd g=dp[0] if k else 0 return int((g ^ b[0] ^ b[-1])!=0) random.seed(1) bad=[] for it in range(5000): n=random.randint(2,10) e=randtree(n) x,y=random.sample(range(n),2) a=game(n,e,x,y) f=formula(n,e,x,y) if a!=f: bad.append((n,e,x,y,a,f,path_weights(n,e,x,y))) break len(bad),bad[:1] (0, [])
pythondef g_state(n,e,mask): adj=[[] for _ in range(n)] for a,b in e:adj[a].append(b);adj[b].append(a) @lru_cache(None) def g(m): for v in range(n): if not m>>v&1 and sum(m>>u&1 for u in adj[v])==1: if g(m|1<<v)==0:return 1 return 0 return g(mask) def no_score_successors(n,e,mask): adj=[[] for _ in range(n)] for a,b in e:adj[a].append(b);adj[b].append(a) out=[] for v in range(n): if not mask>>v&1: c=sum(mask>>u&1 for u in adj[v]) if c!=1: out.append((v,c,g_state(n,e,mask|1<<v))) return out for n in range(4,9): ex=None for _ in range(100): e=randtree(n) for mask in range(1,(1<<n)-1): adj=[[] for _ in range(n)] for a,b in e: adj[a].append(b);adj[b].append(a) if all(sum(mask>>u&1 for u in adj[v])!=1 for v in range(n) if not mask>>v&1): su=no_score_successors(n,e,mask) gs=set(z for _,_,z in su) if gs=={1}: ex=(e,mask,su);break if ex:break print(n,ex)
pythonfor n in range(4,9): bad=None for _ in range(100): e=randtree(n); adj=[[] for _ in range(n)] for a,b in e:adj[a].append(b);adj[b].append(a) for mask in range(1,(1<<n)-1): if any(sum(mask>>u&1 for u in adj[v])==1 for v in range(n) if not mask>>v&1): continue for v in range(n): if not mask>>v&1 and sum(mask>>u&1 for u in adj[v])>=2: if g_state(n,e,mask|1<<v)!=0: bad=(e,mask,v,sum(mask>>u&1 for u in adj[v]));break if bad:break if bad:break print(n,bad)
pythonfor sz in range(1,9): vals=set() for _ in range(200): e=randtree(sz) if sz>1 else [] vals.add(local_game_tree(sz,e,[0,0])) print(sz,vals)
pythonsample='''4 3 1 2 1 2 2 3 4 1 4 1 2 2 3 3 4 3 1 3 1 2 2 3 6 2 3 1 2 1 3 1 4 1 5 1 6 ''' def solve_data(s): it=iter(map(int,s.split())) t=next(it);out=[] for _ in range(t): n=next(it);x=next(it)-1;y=next(it)-1 e=[] for i in range(n-1): a=next(it)-1;b=next(it)-1;e.append((a,b)) out.append(formula(n,e,x,y)) return out solve_data(sample) [1, 1, 0, 0]
pythoncode = r''' import sys input=sys.stdin.readline t=int(input()) for _ in range(t): n,x,y=map(int,input().split()) x-=1 y-=1 a=[[] for _ in range(n)] for i in range(n-1): u,v=map(int,input().split()) u-=1 v-=1 a[u].append(v) a[v].append(u) q=[x] p=[-1]*n p[x]=x for v in q: if v==y: break for u in a[v]: if p[u]==-1: p[u]=v q.append(u) c=[] v=y while v!=x: c.append(v) v=p[v] c.append(x) c.reverse() z=[0]*n for v in c: '1\n1\n0\n0\n'
pythonimport time n=3000 e=[(i,i+1) for i in range(n-1)] st=time.perf_counter() r=formula(n,e,0,n-1) elapsed=time.perf_counter()-st elapsed,r (0.8746384730000045, 1)
python# exhaustive all labeled trees up to n=6, all x<y import heapq, itertools, time, math def tree_from_prufer(p): n=len(p)+2 deg=[1]*n for x in p: deg[x]+=1 h=[i for i in range(n) if deg[i]==1]; heapq.heapify(h) e=[] for x in p: u=heapq.heappop(h);e.append((u,x)) deg[u]-=1;deg[x]-=1 if deg[x]==1:heapq.heappush(h,x) e.append((heapq.heappop(h),heapq.heappop(h))) return e st=time.perf_counter() cnt=0 bad=None for n in range(2,7): seqs=[()] if n==2 else itertools.product(range(n), repeat=n-2) for pseq in seqs: e=tree_from_prufer(pseq) for x in range(n): for y in range(x+1,n): cnt+=1 if game(n,e (0.608857629000056, 20796, None) (4495501, 4492503)
Сначала разложим задачу по структуре, а потом уже код.
Пусть текущее множество вершин со значением 0 — это .
Ход в вершину приносит очко тогда и только тогда, когда у ровно один сосед уже лежит в .
Полезная лемма для дерева:
Если хотя бы одна вершина уже равна
0, то значение оставшейся игры для игрока, который сейчас ходит, всегда равно либо0, либо1.
Если существует ход, приносящий очко, оптимальный ход можно выбирать только среди таких ходов. Если ходов на очко нет, значение позиции равно0.
Это доказывается индукцией по числу оставшихся единиц. Для хода с очком переход имеет вид , для хода без очка — ; после индукционного шага , поэтому нулевой ход не может быть лучше очкового. В случае отсутствия очковых ходов ацикличность дерева позволяет тем же удалением крайних компонент получить значение 0.
Следовательно, исходную игру можно заменить на обычную impartial game:
Если эта вспомогательная игра выигрышная для первого игрока, исходное , иначе .
То есть ответ здесь всегда только 0 или 1.
Рассмотрим единственный путь
Во вспомогательной игре нулевые вершины растут двумя связными областями: от и от .
Вне пути никаких конфликтов быть не может: если ветка уже прикреплена к нулевой вершине пути, её можно целиком постепенно закрасить.
А на самом пути области идут навстречу друг другу. В конце останется ровно одна вершина пути, которую взять нельзя: у неё окажутся сразу два нулевых соседа.
Для каждой обозначим через число вершин во всех ветках, отходящих от неё не по пути.
Нам нужна только чётность .
Почему: игра в такой подвешенной ветке всегда длится ровно столько ходов, сколько в ней вершин. Поэтому её число Шпрага—Гранди:
Пусть
— число Шпрага—Гранди игры, когда между двумя уже занятыми областями остались вершины пути .
Если , единственная вершина уже имеет двух нулевых соседей, поэтому:
Если , можно взять либо левый, либо правый конец.
После взятия :
Значит первый вариант:
Аналогично справа:
Поэтому
где в b сразу храним только чётности.
У и боковые ветки активны изначально, поэтому итоговый SG:
Если он ненулевой — ответ 1, иначе 0.
DP зависит только от предыдущей диагонали, поэтому память .
Для честного перебора маленьких деревьев я использовал состояние mask нулевых вершин и точный minimax:
Проверки:
20796 позиций, расхождений 0;5000/5000 совпадений с полным minimax.Для примера из условия алгоритм получил:
text1 1 0 0
что полностью совпадает.
Путь и размеры подвешенных частей находятся за .
Интервальный DP:
где — длина пути между и .
Память:
При в худшем случае получается примерно 4.5 млн DP-переходов. На тесте-пути из 3000 вершин вычисление заняло около 0.9 с в текущем окружении. При лимите 2 с, сумме и памяти 256 МБ укладывается.
pythonimport sys it=iter(map(int,sys.stdin.buffer.read().split())) t=next(it) o=[] for _ in range(t): n=next(it) x=next(it)-1 y=next(it)-1 a=[[] for _ in range(n)] for i in range(n-1): u=next(it)-1 v=next(it)-1 a[u].append(v) a[v].append(u) p=[-1]*n p[x]=x q=[x] for v in q: if v==y: break for u in a[v]: if p[u]==-1: p[u]=v q.append(u) c=[] v=y while v!=x: c.append(v) v=p[v] c.append(x) c.reverse() z=[0]*n for v in c: z[v]=1 b=[0]*len(c) for i,v in enumerate(c): for u in a[v]: if z[u]: continue s=[(u,v)] while s: w,pa=s.pop() b[i]^=1 for h in a[w]: if h!=pa: s.append((h,w)) m=len(c)-2 if m<=1: g=0 else: d=[0]*m for k in range(2,m+1): e=[0]*(m-k+1) for i in range(m-k+1): l=i+1 r=i+k u=d[i+1]^b[l] v=d[i]^b[r] if u and v: w=0 elif u!=1 and v!=1: w=1 else: w=2 e[i]=w d=e g=d[0] o.append(str(int((g^b[0]^b[-1])!=0))) sys.stdout.write('\n'.join(o))