做手速狗的一场,E差5min调出来,不过还是苟到了rk4。
略
略
令 C ( S ) = ∑ i ∈ S c i C(S)=\sum_{i \in S}c_i C(S)=∑i∈Sci,即 f ( S ) = C ( N ( S ) ) f(S)=C(N(S)) f(S)=C(N(S))。
根据容斥原理,我们知道 f ( S ) = C ( N ( S ) ) = ∑ T ⊆ S ( − 1 ) ∣ T ∣ − 1 ⋅ C ( N ( T 1 ) ∩ N ( T 2 ) ∩ . . . ∩ N ( T ∣ T ∣ ) ) f(S)=C(N(S))=\sum_{T \subseteq S}(-1)^{|T|-1}\cdot C(N(T_1) \cap N(T_2) \cap ... \cap N(T_{|T|})) f(S)=C(N(S))=∑T⊆S(−1)∣T∣−1⋅C(N(T1)∩N(T2)∩...∩N(T∣T∣))。容易按 ∣ T ∣ |T| ∣T∣从小到大归纳证明 ∀ S , k ∣ f ( S ) \forall S,k|f(S) ∀S,k∣f(S)当且仅当 ∀ T , k ∣ C ( N ( T 1 ) ∩ N ( T 2 ) ∩ . . . ∩ N ( T ∣ T ∣ ) ) \forall T,k|C(N(T_1) \cap N(T_2) \cap ... \cap N(T_{|T|})) ∀T,k∣C(N(T1)∩N(T2)∩...∩N(T∣T∣))。
进一步的,令 N ′ ( i ) N'(i) N′(i)为右部图中点 i i i在左部图中相邻的点的集合,可以证明答案即为 ∀ S , gcd ( ∑ N ′ ( i ) = S c i ) \forall S,\gcd (\sum_{N'(i)=S}c_i) ∀S,gcd(∑N′(i)=Sci),于是问题变为将右部图中的点按 N ′ N' N′分类。
这里可以用哈希实现,单组数据时间复杂度 O ( n + m ) \mathcal O(n+m) O(n+m)。或者直接将每个点的邻边用vector存起来,然后弄个 m a p < v e c t o r , l o n g l o n g > map
#include
#define MOD1 170215161
#define MOD2 151728211
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
map <pr,ll> mp;
ll num[500005];
int val1[500005],val2[500005];
int randint1() {
return ((ll)rand()*89543534534LL+rand())%MOD1;
}
int randint2() {
return ((ll)rand()*89543534534LL+rand())%MOD2;
}
int sum1[500005],sum2[500005];
int main() {
srand(chrono::steady_clock::now().time_since_epoch().count());
int cases;
scanf("%d",&cases);
for(;cases;cases--) {
mp.clear();
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&num[i]);
for(int i=1;i<=n;i++) {
val1[i]=randint1();
val2[i]=randint2();
sum1[i]=sum2[i]=0;
}
for(int i=1;i<=m;i++) {
int x,y;
scanf("%d%d",&x,&y);
sum1[y]=(sum1[y]+val1[x])%MOD1;
sum2[y]=(sum2[y]+val2[x])%MOD2;
}
for(int i=1;i<=n;i++)
if (sum1[i]&&sum2[i]) mp[pr(sum1[i],sum2[i])]+=num[i];
ll ans=0;
for(map<pr,ll>::iterator it=mp.begin();it!=mp.end();it++)
ans=__gcd(ans,it->second);
printf("%lld\n",ans);
}
return 0;
}
令第 i i i个人的权值是 2 l i 2^{l_i} 2li,那么贡献过程可以理解为二进制加法,每次进位时有贡献,于是顺序是不会影响结果的。
那么可以考虑从后往前dp,设 F [ i ] [ j ] [ k ] F[i][j][k] F[i][j][k]表示从后往前考虑到第 i i i个人,此前取的最大的 l i l_i li为 j j j,且对后面选择的 2 l i 2^{l_i} 2li做完二进制加法(只做到第 l i l_i li位)后,该位剩余 k k k的最小贡献和,转移考虑第 i i i个人是否选择即可。这个dp看起来是三次方的,但是注意到后两维相当于模拟了一个所有数的二进制加法,因此对于一个 i i i,有效的状态是 O ( n + m ) \mathcal O(n+m) O(n+m)的。
时间复杂度为 O ( n ( n + m ) ) \mathcal O(n(n+m)) O(n(n+m))。
#include
#define inf 0x3f3f3f3f3f3f3f3fLL
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
vector<ll> f[2][2005];
int c[4005],w[2005],num[2005];
ll g[2005],val[4005][2005];
int vis[4005][2005],up[2005][2005],cnt;
void dfs(int x,int y,int t) {
if (vis[x][y]==cnt) return;
vis[x][y]=cnt;
if (x==t||!y) {
val[x][y]=0;
up[x][y]=y;
return;
}
dfs(x+1,y>>1,t);
val[x][y]=val[x+1][y>>1]+(ll)y*c[x];
up[x][y]=up[x+1][y>>1];
}
int main() {
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&num[i]);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
for(int i=1;i<=n+m;i++) scanf("%d",&c[i]);
int cur=0;
for(int i=n;i>0;i--) {
cur^=1;
for(int j=1;j<=m;j++) f[cur][j]=f[cur^1][j];
int maxd=1;
for(int j=0;j<=n;j++) g[j]=-inf;
g[1]=-w[i];
cnt++;
for(int j=1;j<=num[i];j++)
for(int k=1;k<f[cur^1][j].size();k++)
if (f[cur^1][j][k]>-inf) {
dfs(j,k,num[i]);
int t=up[j][k]+1;
maxd=max(maxd,t);
g[t]=max(g[t],f[cur^1][j][k]+val[j][k]-w[i]);
}
maxd=max(maxd,(int)f[cur][num[i]].size()-1);
for(int j=1;j<f[cur][num[i]].size();j++) g[j]=max(g[j],f[cur][num[i]][j]);
f[cur][num[i]].resize(maxd+1);
for(int j=0;j<f[cur][num[i]].size();j++) f[cur][num[i]][j]=g[j];
}
cnt++;
ll ans=0;
for(int i=1;i<=m;i++)
for(int j=1;j<f[cur][i].size();j++)
if (f[cur][i][j]>-inf) {
dfs(i,j,n+m+1);
ans=max(ans,f[cur][i][j]+val[i][j]);
}
printf("%lld\n",ans);
return 0;
}
考虑枚举一个 k k k,将 < k
那么考虑 01 01 01序列 a ′ a' a′,令 a 0 ′ = a 1 ′ a'_0=a'_1 a0′=a1′, a n + 1 ′ = a n ′ a'_{n+1}=a'_n an+1′=an′,我们发现对于一段连续的长度 > 1 >1 >1的 0 0 0或者 1 1 1,里面的 a ′ a' a′都不会改变,称这样的一个连续段为“柱子”。则柱子间形成若干个 01 01 01交替的连续段,可以发现每轮操作会将这样的连续段从左右缩进去一个单位(最左最右的 a ′ a' a′与旁边的柱子合并了),这样容易得到最终的序列。
现在考虑扫描线,按权值从小到大考虑所有的 a i a_i ai,每次将初始的 a ′ a' a′中一个 0 0 0变为 1 1 1,这样最终的 a ′ a' a′也会将若干个 0 0 0变为 1 1 1。注意到此时最终的 a ′ a' a′的改变是在修改前最终的 a ′ a' a′的基础上,将至多一个区间中的 0 0 0全部变为 1 1 1(这些位置最终的答案即为 a i a_i ai)。用set和并查集维护初始的 a ′ a' a′中所有的柱子,即可快速知道最终修改的区间,进而维护最终的 a ′ a' a′,更新最终的 a a a以及答案。
时间复杂度为 O ( n log n ) \mathcal O(n\log n) O(nlogn)。
#include
#define inf 0x3f3f3f3f3f3f3f3fLL
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
struct SETS {
int fa[500005];
void init(int n) {
for(int i=1;i<=n;i++) fa[i]=i;
}
int find_father(int x) {
return (fa[x]==x)?x:fa[x]=find_father(fa[x]);
}
void merge(int x,int y) {
x=find_father(x);y=find_father(y);
if (x==y) return;
fa[x]=y;
}
} st1,st2;
set <int> bel,equ;
int num[500005],ans[500005],maxn,n;
int fir[500005],id[500005];
void change1(int x,bool v) {
if (x==1||num[x]==num[x-1]) equ.erase(x-1);
if (x==n||num[x]==num[x+1]) equ.erase(x);
num[x]=1;
if (x==1||num[x]==num[x-1]) equ.insert(x-1);
if (x==n||num[x]==num[x+1]) equ.insert(x);
if (x==1) num[x-1]=1;
if (x==n) num[x+1]=1;
if (x>1&&num[x-1]) {
st1.merge(x,x-1);
st2.merge(x-1,x);
}
if (x<n&&num[x+1]) {
st2.merge(x,x+1);
st1.merge(x+1,x);
}
}
void change2(int x,bool v) {
int l=st1.find_father(x),r=st2.find_father(x);
if (l<r||x==1||x==n) {
set<int>::iterator it=equ.lower_bound(l);
if (l>1&&it!=equ.begin()) {
it--;
int t=(*it)+1;
if (num[t]) {
if (v) maxn=max(maxn,(l-t)>>1);
l=t+1;
}
else {
if (v) maxn=max(maxn,(l-t)>>1);
l=((t+l)>>1)+1;
}
}
it=equ.upper_bound(r);
if (r<n&&it!=equ.end()) {
int t=(*it);
if (num[t]) {
if (v) maxn=max(maxn,(t-r)>>1);
r=t-1;
}
else {
if (v) maxn=max(maxn,(t-r)>>1);
r=((t+r)>>1);
}
}
}
else {
set<int>::iterator it1=equ.lower_bound(x);
it1--;
set<int>::iterator it2=equ.upper_bound(x);
int nl=(*it1)+1,nr=(*it2);
if (v) maxn=max(maxn,(nr-nl)>>1);
if (num[nl]&&num[nr]) {
l=nl+1;
r=nr-1;
}
else if (num[nl]) {
l=nl+1;
r=((nl+nr)>>1);
}
else if (num[nr]) {
l=((nl+nr)>>1)+1;
r=nr-1;
}
else {
l=1;r=0;
}
}
if (l<=r) {
for(;;) {
set<int>::iterator it=bel.lower_bound(l);
if (it!=bel.end()&&(*it)<=r) {
ans[*it]=fir[x];
bel.erase(it);
}
else break;
}
}
}
bool cmp(int x,int y) {
return fir[x]<fir[y];
}
int main() {
scanf("%d",&n);
for(int i=1;i<=n;i++) {
scanf("%d",&fir[i]);
id[i]=i;
bel.insert(i);
}
sort(id+1,id+n+1,cmp);
st1.init(n);st2.init(n);
for(int i=0;i<=n;i++) equ.insert(i);
int r;
for(int i=1;i<=n;i=r+1)
if (fir[id[i]]>fir[id[i-1]]) {
r=i;
while (r<n&&fir[id[r+1]]==fir[id[r]]) r++;
for(int j=i;j<=r;j++)
change1(id[j],1);
for(int j=i;j<=r;j++)
change2(id[j],1);
}
printf("%d\n",maxn);
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
printf("\n");
return 0;
}
忽略没有被路径覆盖的边,我们可以给第 i i i条边设一个 01 01 01变量 p i p_i pi, p i = 0 p_i=0 pi=0代表 c u i < c v i c_{u_i}
可以发现一条路径给定了路径上相邻边之间的 p p p相等或相异的关系,可以简单建出边得到一个图,若得到的图不为二分图显然无解,否则可以将原树划分为若干连通块,第 i i i个连通块有个变量 q i q_i qi,连通块内部的 p p p均为 q i q_i qi或 ¬ q i \lnot q_i ¬qi。
考虑二分答案 k k k,判定能否用 1 1 1到 k k k覆盖所有的 c i c_i ci。
显然可以得到一个dp,令 F [ i ] [ 0 / 1 ] [ j ] F[i][0/1][j] F[i][0/1][j]表示考虑了点 i i i的子树,点 i i i所在连通块的 v v v为 0 / 1 0/1 0/1,且 c i = j c_i=j ci=j是否可行。直接实现复杂度过高,不过发现由于某条边的权值可以取任意正数,因此由儿子推到父亲后 v v v为 0 / 1 0/1 0/1可行的 j j j是 1 1 1到 k k k的一个前缀或后缀(事实上 F [ i ] [ 0 / 1 ] [ j ] = 1 F[i][0/1][j]=1 F[i][0/1][j]=1的 j j j形成至多两个区间),那么只需要维护 F [ i ] [ 0 / 1 ] [ j ] = 1 F[i][0/1][j]=1 F[i][0/1][j]=1的最小/最大的 j j j即可。并且注意到 v v v取 0 / 1 0/1 0/1是对称的,只需维护 F [ i ] [ 0 ] F[i][0] F[i][0]即可。
构造方案也可以类似dp,时间复杂度为 O ( n log n ) \mathcal O(n\log n) O(nlogn)。
#include
#define FR first
#define SE second
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
inline pr merge(pr x,pr y) {
return pr(max(x.FR,y.FR),min(x.SE,y.SE));
}
vector <int> e[500005];
int fa[500005][20],dep[500005];
void dfs1(int x) {
for(int i=0;i<e[x].size();i++)
if (e[x][i]!=fa[x][0]) {
int u=e[x][i];
fa[u][0]=x;dep[u]=dep[x]+1;
for(int j=1;j<20;j++) fa[u][j]=fa[fa[u][j-1]][j-1];
dfs1(u);
}
}
int lca(int x,int y) {
if (dep[x]<dep[y]) swap(x,y);
int d=dep[x]-dep[y];
for(int i=0;i<20;i++)
if ((d>>i)&1) x=fa[x][i];
if (x==y) return x;
for(int i=19;i>=0;i--)
if (fa[x][i]!=fa[y][i]) {
x=fa[x][i];
y=fa[y][i];
}
return fa[x][0];
}
int jump(int x,int d) {
for(int i=0;i<20;i++)
if ((d>>i)&1) x=fa[x][i];
return x;
}
vector <pr> ee[500005];
int size[500005];
void dfs2(int x,int fa) {
for(int i=0;i<e[x].size();i++)
if (e[x][i]!=fa) {
int u=e[x][i];
dfs2(u,x);
size[x]+=size[u];
}
if (size[x]) {
ee[x].push_back(pr(fa,0));
ee[fa].push_back(pr(x,0));
}
}
int col[500005],id[500005];
void dfs3(int x) {
for(int i=0;i<ee[x].size();i++) {
int u=ee[x][i].FR;
if (!id[u]) {
id[u]=id[x];
col[u]=col[x]^ee[x][i].SE;
dfs3(u);
}
else if ((col[x]^col[u])!=ee[x][i].SE) {
puts("-1");
exit(0);
}
}
}
pr f[500005],g[500005];
int vis[500005];
bool dfs4(int x,int fa,int k) {
for(int i=0;i<e[x].size();i++)
if (e[x][i]!=fa) {
int u=e[x][i];
if (!dfs4(u,x,k)) return 0;
}
f[x]=pr(1,k);
int sz=0;
for(int i=0;i<e[x].size();i++)
if (e[x][i]!=fa&&id[e[x][i]]) {
int u=e[x][i];
if (id[u]==id[x]) {
if (!col[u]) f[x]=merge(f[x],pr(1,f[u].SE-1));
else f[x]=merge(f[x],pr(f[u].FR+1,k));
}
else {
if (!vis[id[u]]) {
vis[id[u]]=++sz;
g[sz]=pr(1,k);
}
int t=vis[id[u]];
if (!col[u]) g[t]=merge(g[t],pr(1,f[u].SE-1));
else g[t]=merge(g[t],pr(f[u].FR+1,k));
}
}
for(int i=0;i<e[x].size();i++)
if (e[x][i]!=fa) vis[id[e[x][i]]]=0;
if (f[x].FR>f[x].SE) return 0;
pr u(1,k);
for(int i=1;i<=sz;i++) {
if (g[i].FR>g[i].SE) return 0;
if (k-g[i].SE+1<g[i].FR) g[i]=pr(k-g[i].SE+1,k-g[i].FR+1);
u=merge(u,g[i]);
}
pr t1=merge(u,f[x]),t2=merge(pr(k-u.SE+1,k-u.FR+1),f[x]);
if (t1.FR>t1.SE&&t2.FR>t2.SE) return 0;
if (t1.FR>t1.SE) f[x]=t2;
else if (t2.FR>t2.SE) f[x]=t1;
else f[x]=pr(min(t1.FR,t2.FR),max(t1.SE,t2.SE));
return 1;
}
int ans[500005],val[500005];
bool d[500005];
void dfs5(int x,int fa,int k,bool v) {
int sz=0;
for(int i=0;i<e[x].size();i++)
if (e[x][i]!=fa&&id[e[x][i]]!=id[x]) {
int u=e[x][i];
if (!vis[id[u]]) {
vis[id[u]]=++sz;
val[sz]=id[u];
g[sz]=pr(1,k);
}
int t=vis[id[u]];
if (!col[u]) g[t]=merge(g[t],pr(1,f[u].SE-1));
else g[t]=merge(g[t],pr(f[u].FR+1,k));
}
for(int i=0;i<e[x].size();i++)
if (e[x][i]!=fa) vis[id[e[x][i]]]=0;
for(int i=1;i<=sz;i++) d[val[i]]=((g[i].FR<=ans[x]&&g[i].SE>=ans[x])?0:1);
for(int i=0;i<e[x].size();i++)
if (e[x][i]!=fa) {
int u=e[x][i];
if (!id[u]) {
ans[u]=f[u].FR;
dfs5(u,x,k,0);
}
else {
bool c=((id[u]==id[x])?v:d[id[u]]);
if (!c) ans[u]=((!col[u])?f[u].SE:f[u].FR);
else ans[u]=((col[u])?k-f[u].FR+1:k-f[u].SE+1);
dfs5(u,x,k,c);
}
}
}
int main() {
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++) {
int x,y;
scanf("%d%d",&x,&y);
e[x].push_back(y);
e[y].push_back(x);
}
dfs1(1);
for(int i=1;i<=m;i++) {
int x,y;
scanf("%d%d",&x,&y);
int p=lca(x,y),u,v;
if (p!=x) {
size[x]++;
size[u=jump(x,dep[x]-dep[p]-1)]--;
}
if (p!=y) {
size[y]++;
size[v=jump(y,dep[y]-dep[p]-1)]--;
}
if (p!=x&&p!=y) {
ee[u].push_back(pr(v,1));
ee[v].push_back(pr(u,1));
}
}
dfs2(1,0);
int cnt=0;
for(int i=2;i<=n;i++)
if (!id[i]) {
id[i]=++cnt;
dfs3(i);
}
int l=1,r=n;
while (l<r) {
int mid=((l+r)>>1);
bool v=dfs4(1,0,mid);
if (v) r=mid; else l=mid+1;
}
dfs4(1,0,l);
ans[1]=f[1].FR;
dfs5(1,0,l,0);
printf("%d\n",l);
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
printf("\n");
return 0;
}
/*
3 1
1 2
1 3
2 3
*/