[hdu7078]Pty with card
显然问题被分为两部分,先考虑如何求F(n)——
令第一次所选的人编号为1,其之后所有人按顺时针依次编号为2,3,...,n,那么用一个序列来描述状态,其中第i个元素为当前存在的人中编号第i小的人手牌数(显然序列长度即为剩余人数)
初始序列显然为{1,1,...,1}(共n个1),并对n的奇偶性分类讨论:
1.若n为奇数,则n轮后序列为{3,2,2,...,2}(其中共n−32个2)
2.若n为偶数,则n轮后序列为{4,2,2,...,2}(其中共n2−2个2)
(关于这个结果,手动模拟若干次即可得到规律)
注意到此时所有元素都≥2,那么若序列长度为2m+1(其中m∈Z+),循环节即恰为4m+2
关于这个性质,考虑两轮中每一个人都会在奇数轮操作一次、偶数轮操作一次,那么总共即恰好失去3张卡片并得到3张卡片,因此卡牌数量不变,且由于初始有两张卡片,不会有人"出局"
下面,考虑序列长度为2m,再对两类分别讨论:
1.若n为奇数(注意不是m),则2m轮后序列为{2,3,1,3,1,3...,1,3}(其中共m−1对1,3),再2m轮后序列为{5,4,4,...,4}(其中共m−1个4)
不难发现如果序列长度仍是偶数,其又会变为{9,8,8,...,8},{17,16,16,...,16},...(可以归纳证明),直至序列长度为奇数(答案为序列长度的两倍)
2.若n为偶数,类似的4m轮后序列为{6,4,4,...,4}(其中共m−1个4),如果序列长度仍是偶数,其又会变为{10,8,8,...,8},{18,16,16,...,16},...,直至序列长度为奇数
(另外,若最终序列长度为1则F(n)=0)
综上,有
F(n)={0(n≤2)∨(lowbit(m)=1)2mlowbit(m)(n≥3)∧(lowbit(m)≠1)
(其中m=⌊n−12⌋,lowbit(m)指m二进制下最低位上的1对应的值)
接下来,考虑如何求∀1≤x≤n,∑ni=1F(vi+d(i,x))——
将其点分治,问题即是要维护一个集合S,支持:1.加入一个元素x;2.(给定x)查询∑y∈SF(x+y)
这个并不容易维护,但注意到查询中x即为某点到当前点分中心的距离,是连续变化的,因此这个问题还可以看作支持:1.加入一个元素x;2.令所有元素+1;3.查询∑x∈SF(x)
维护一棵trie树,并且从低到高存储数字,依次考虑这些操作:
1.加入一个元素x,与普通的trie树相同
2.令所有元素+1,即不断交换左右儿子,并递归(新的)左儿子即可
3.查询∑x∈SF(x),不断递归左儿子,维护子树中所有元素的和即可(注意去掉lowbit(m)=1的情况)
由此,单次操作时间复杂度为o(logn),总复杂度即o(nlog2n),可以通过

1 #include<bits/stdc++.h> 2 using namespace std; 3 #define N 100005 4 #define ll long long 5 struct Edge{ 6 int nex,to; 7 }edge[N<<1]; 8 vector<int>v[N]; 9 int E,rt,t,n,x,y,mx,a[N],head[N],vis[N],sz[N],f[N<<2]; 10 ll ans[N]; 11 int lowbit(int k){ 12 return (k&(-k)); 13 } 14 namespace Trie{ 15 int V,tag,st[N],L[N*6],sz[N*6],ch[N*6][2]; 16 ll sum[N*6]; 17 int New(){ 18 int k=++V; 19 L[k]=sz[k]=sum[k]=ch[k][0]=ch[k][1]=0; 20 return k; 21 } 22 ll get(int k){ 23 return sum[k]+(ll)tag*sz[k]; 24 } 25 void init(){ 26 V=tag=0; 27 New(); 28 } 29 void add_val(int x){ 30 st[0]=st[1]=1; 31 for(int i=0,k=1;i<18;i++){ 32 int p=((x>>i)&1); 33 if (!ch[k][p])ch[k][p]=New(); 34 k=st[++st[0]]=ch[k][p]; 35 } 36 for(int i=1;i<=st[0];i++)sz[st[i]]++,sum[st[i]]+=x; 37 L[st[st[0]]]=st[st[0]]; 38 for(int i=st[0]-1;i;i--)L[st[i]]=L[ch[st[i]][0]]; 39 } 40 void Add(){ 41 tag++; 42 st[0]=st[1]=1; 43 for(int i=0,k=1;(i<18)&&(k);i++){ 44 swap(ch[k][0],ch[k][1]); 45 k=st[++st[0]]=ch[k][0]; 46 } 47 L[st[st[0]]]=st[st[0]]; 48 for(int i=st[0]-1;i;i--)L[st[i]]=L[ch[st[i]][0]]; 49 } 50 ll query(){ 51 ll ans=0; 52 for(int i=1,k=ch[1][0];(i<18)&&(k);i++){ 53 ans+=(get(ch[k][1])-get(L[ch[k][1]])>>i-1); 54 k=ch[k][0]; 55 } 56 tag--; 57 for(int i=1,k=ch[1][1];(i<18)&&(k);i++){ 58 ans+=(get(ch[k][1])-get(L[ch[k][1]])>>i-1); 59 k=ch[k][0]; 60 } 61 tag++; 62 return ans; 63 } 64 } 65 void add_edge(int x,int y){ 66 edge[E].nex=head[x]; 67 edge[E].to=y; 68 head[x]=E++; 69 } 70 void get_sz(int k,int fa){ 71 sz[k]=1; 72 for(int i=head[k];i!=-1;i=edge[i].nex) 73 if ((!vis[edge[i].to])&&(edge[i].to!=fa)){ 74 get_sz(edge[i].to,k); 75 sz[k]+=sz[edge[i].to]; 76 } 77 } 78 void get_rt(int k,int fa,int s){ 79 int mx=s-sz[k]; 80 for(int i=head[k];i!=-1;i=edge[i].nex) 81 if ((!vis[edge[i].to])&&(edge[i].to!=fa)){ 82 get_rt(edge[i].to,k,s); 83 mx=max(mx,sz[edge[i].to]); 84 } 85 if (mx<=(s>>1))rt=k; 86 } 87 void get_val(int k,int fa,int s){ 88 if (mx<s)v[++mx].clear(); 89 v[s].push_back(k); 90 Trie::add_val(a[k]+s); 91 for(int i=head[k];i!=-1;i=edge[i].nex) 92 if ((!vis[edge[i].to])&&(edge[i].to!=fa))get_val(edge[i].to,k,s+1); 93 } 94 void calc(int k,int p){ 95 Trie::init(); 96 mx=0,v[0].clear(); 97 get_val(k,0,p); 98 p=1-(p<<1); 99 for(int i=0;i<=mx;i++){ 100 ll s=Trie::query(); 101 for(int j=0;j<v[i].size();j++)ans[v[i][j]]+=p*s; 102 Trie::Add(); 103 } 104 } 105 void dfs(int k){ 106 get_sz(k,0); 107 get_rt(k,0,sz[k]); 108 calc(rt,0); 109 vis[rt]=1; 110 for(int i=head[rt];i!=-1;i=edge[i].nex) 111 if (!vis[edge[i].to])calc(edge[i].to,1); 112 for(int i=head[rt];i!=-1;i=edge[i].nex) 113 if (!vis[edge[i].to])dfs(edge[i].to); 114 } 115 int main(){ 116 for(int i=2;i<(N<<2);i++) 117 if (lowbit(i>>1)==1)f[i]=0; 118 else f[i]=((i>>1)/lowbit(i>>1)<<1); 119 scanf("%d",&t); 120 while (t--){ 121 scanf("%d",&n); 122 E=0; 123 memset(head,-1,sizeof(head)); 124 memset(vis,0,sizeof(vis)); 125 memset(ans,0,sizeof(ans)); 126 for(int i=1;i<=n;i++){ 127 scanf("%d",&a[i]); 128 a[i]--; 129 } 130 for(int i=1;i<n;i++){ 131 scanf("%d%d",&x,&y); 132 add_edge(x,y); 133 add_edge(y,x); 134 } 135 dfs(1); 136 for(int i=1;i<n;i++)printf("%lld ",ans[i]); 137 printf("%lld\n",ans[n]); 138 } 139 return 0; 140 }
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步