【比赛链接】http://59.61.75.5:8018/contest/223
A. 欧拉路径树
【题解】
考虑 dfs 依次处理每个子树.
显然对于每一种数字,相邻两个同种数字间的即为该节点的一个子树. 递归处理.
现在考虑一种情况:$1 2 \dots 2 -1 -1 1$. 假设 $3$ 还没出现过,则有两种填法:$1 2 \dots 2 1 3 1$ 和 $1 2 \dots 2 3 2 1$.
注意到这两种填法分别对应的树的形态为 $3$ 为 $2$ 子节点及 $3$ 为 $1$ 子节点. 画图分析知,这两种情况对其它任意节点没有区别.
因此我们考虑贪心,尽可能地多填当前的 $1$. 即使每棵子树尽量小,子树个数尽量多.
显然先递归处理 $2 \dots 2$ 这棵子树,递归回来后可压缩成单个节点 $2$.
现在考虑处理 $1 2 -1 -1 1$ 类型的问题. 根据树的性质,应该用一个栈来维护.
若碰到一个非 $-1$ 的数,且当前数出现过(可能是 $-1$ 造成的),则判断与栈顶的下一个数是否相等,相等即可将栈顶弹出,否则入栈.
对于 $-1$,存在一种情况使得必须填后面出现过的数:$1 -1 3 2 1$. 这时候必须填 $2$. 分析可得:当 $2(sum_{j-1}-sum_{i})=j-i$ 且 $i,j$ 奇偶性相同时,可以填入 $a[j]$.
上述式子可转化为 $2 sum_{i}-i=2sum{j-1}-j$. 用数据结构(splay 套 splay map 套 set)分奇偶性维护即可.
因此对于每个 $-1$ 有:若能找到能填的位置就填入,并压栈;否则弹栈即可. 若栈空则选一个未使用过的数填入.
效率 $O(n \log n)$ 或 $O(n \log^2 n)$(根据数据结构的使用而定,可能存在线性做法). 期望得分:100.
【代码】
1 #include<bits/stdc++.h> 2 inline int read ( void ) 3 { 4 int x=0;char ch;bool f=true; 5 while ( !isdigit(ch=getchar()) ) if ( ch=='-' ) f=false; 6 for ( x=ch^48;isdigit(ch=getchar()); ) x=(x<<1)+(x<<3)+(ch^48); 7 return f ? x : -x ; 8 } 9 const int maxn=1000000+10; 10 int a[maxn],st[maxn],tp,n,sum[maxn],vis[maxn],times; 11 std::map<int,std::set<int>> s[2]; 12 std::queue<int> unused; 13 std::vector<int> pos[maxn]; 14 inline void solve ( int l,int r,int x ) 15 { 16 std::vector<int> G; 17 for ( int i=l;i<=r;i++ ) 18 if ( ~a[i] ) 19 { 20 for ( int j=0;j<(int)pos[a[i]].size()-1;j++ ) solve(pos[a[i]][j]+1,pos[a[i]][j+1]-1,a[i]); 21 G.push_back(a[i]);i=pos[a[i]].back(); 22 } 23 else G.push_back(-i); 24 st[tp=1]=x;sum[0]=(G[0]>0);s[0].clear();s[1].clear();++times; 25 for ( int i=1;i<(int)G.size();i++ ) 26 if ( G[i]>0 ) sum[i]=sum[i-1]+1,s[i&1][i-2*sum[i-1]].insert(i); 27 else sum[i]=sum[i-1]; 28 for ( int i=0;i<(int)G.size();i++ ) 29 if ( G[i]>0 ) 30 { 31 s[i&1][i-2*sum[i-1]].erase(i); 32 if ( tp>1 and st[tp-1]==G[i] ) tp--; 33 else st[++tp]=G[i]; 34 } 35 else 36 { 37 if ( !s[i&1][i-2*sum[i]].empty() ) 38 { 39 int j=*s[i&1][i-2*sum[i]].begin(); 40 a[-G[i]]=G[j]; 41 if ( vis[G[j]]==times ) while ( st[tp]!=G[j] ) tp--; 42 else vis[G[j]]=times,st[++tp]=G[j]; 43 } 44 else 45 { 46 if ( tp>1 ) a[-G[i]]=st[--tp]; 47 else st[++tp]=a[-G[i]]=unused.front(),unused.pop(); 48 } 49 } 50 } 51 signed main() 52 { 53 for ( int T=read();T--; ) 54 { 55 n=read(); 56 for ( int i=1;i<(n<<1);i++ ) a[i]=read(); 57 a[1]=a[2*n-1]=1; 58 for ( int i=1;i<(n<<1);i++ ) if ( ~a[i] ) pos[a[i]].push_back(i); 59 for ( int i=1;i<=n;i++ ) if ( pos[i].empty() ) unused.push(i); 60 for ( int i=0;i<(int)pos[1].size()-1;i++ ) solve(pos[1][i]+1,pos[1][i+1]-1,1); 61 for ( int i=1;i<(n<<1);i++ ) printf("%d%c",a[i]," \n"[i==2*n-1]); 62 for ( int i=1;i<=n;i++ ) pos[i].clear(); 63 } 64 return 0; 65 }
C. 旅行
【题解】
最近的测试越来越有 FJOI 的风格了。。。
考虑 $k$ 非常小,可用类似折半搜索的做法.
强行令环拆成两半,一半染成黑色,另一半染成白色. 对黑白色分别求解最长链再处理即可.
考虑怎么对所有点染色. 显然只能采用随机化的方法. 因此考虑随机 $T$ 次进行求解.
对于染色完后的处理,显然最大只需要处理到 $l=5$ ($l$ 为链上点数)的情况,则有:
$l=2$,枚举每条边.
$l=3$,枚举两条边判断是否有公共点,有就更新.
$l=4$,先预处理所有边两点间的距离,然后考虑枚举两条边,若两条边无共点则用其中各一个节点间的预处理距离加上两边边权即可.
$l=5$,考虑边 $(u,v),(x,y)$. 若有贡献显然为 $(u,v,p,x,y)$ 型贡献,则对于 $v,x$ 需要算 $3$ 个点的最长链. 注意不可重复,因此需要维护前三长链.
第一轮枚举两条边处理前三长链,第二轮枚举两条边算答案即可.
最后算答案枚举两条跨色边算答案. 效率 $O(Tm^2)$.
当 $T=1000$ 时答案出错概率非常小(错了再交一次就行),且复杂度优秀. 期望得分:0~100(如果非酋可能 $0$)。
【代码】
1 #include<bits/stdc++.h> 2 const long long inf=1LL<<60; 3 int n,m,k,u[400],v[400],c[400],pos[400][400][3]; 4 long long w[400],dis[400][400],Dis[2][400][400]; 5 inline void solve2 ( int color ) 6 { 7 for ( int i=1;i<=m;i++ ) if ( c[u[i]]==color and c[v[i]]==color ) Dis[color][u[i]][v[i]]=Dis[color][v[i]][u[i]]=w[i]; 8 } 9 inline void solve3 ( int color ) 10 { 11 for ( int i=1;i<=m;i++ ) if ( c[u[i]]==color and c[v[i]]==color ) 12 for ( int j=1;j<=m;j++ ) if ( c[u[j]]==color and c[v[j]]==color ) if ( i!=j ) 13 { 14 if ( u[i]==u[j] ) Dis[color][v[i]][v[j]]=std::max(Dis[color][v[i]][v[j]],w[i]+w[j]); 15 else if ( u[i]==v[j] ) Dis[color][v[i]][u[j]]=std::max(Dis[color][v[i]][u[j]],w[i]+w[j]); 16 else if ( v[i]==u[j] ) Dis[color][u[i]][v[j]]=std::max(Dis[color][u[i]][v[j]],w[i]+w[j]); 17 else if ( v[i]==v[j] ) Dis[color][u[i]][u[j]]=std::max(Dis[color][u[i]][u[j]],w[i]+w[j]); 18 } 19 } 20 inline void solve4 ( int color ) 21 { 22 for ( int i=1;i<=m;i++ ) if ( c[u[i]]==color and c[v[i]]==color ) 23 for ( int j=1;j<=m;j++ ) if ( c[u[j]]==color and c[v[j]]==color ) 24 if ( i!=j ) if ( u[i]!=u[j] and u[i]!=v[j] and v[i]!=u[j] and v[i]!=v[j] ) 25 Dis[color][u[i]][u[j]]=std::max(Dis[color][u[i]][u[j]],dis[v[i]][v[j]]+w[i]+w[j]), 26 Dis[color][u[i]][v[j]]=std::max(Dis[color][u[i]][v[j]],dis[v[i]][u[j]]+w[i]+w[j]), 27 Dis[color][v[i]][u[j]]=std::max(Dis[color][v[i]][u[j]],dis[u[i]][v[j]]+w[i]+w[j]), 28 Dis[color][v[i]][v[j]]=std::max(Dis[color][v[i]][v[j]],dis[u[i]][u[j]]+w[i]+w[j]); 29 } 30 inline void solve5 ( int color ) 31 { 32 std::function < void ( int,int,int ) > Addedge = [&] ( int x,int y,int z ) { 33 int rnk=0; 34 while ( rnk<3 ) 35 { 36 int u=pos[x][y][rnk]; 37 if ( dis[x][u]+dis[u][y]<dis[x][z]+dis[z][y] ) break; 38 rnk++; 39 } 40 if ( rnk<3 ) 41 { 42 for ( int i=2;i>rnk;i-- ) pos[x][y][i]=pos[x][y][i-1]; 43 pos[x][y][rnk]=z; 44 } 45 }; 46 std::function < void ( int,int,int,int ) > work = [&] ( int x,int y,int u,int v ) { 47 int rnk = 0; 48 while ( pos[u][v][rnk]==x or pos[u][v][rnk]==y ) rnk++; 49 int z=pos[u][v][rnk]; 50 Dis[color][x][y]=std::max(Dis[color][x][y],dis[x][u]+dis[u][z]+dis[z][v]+dis[v][y]); 51 }; 52 for ( int i=1;i<=n;i++ ) if ( c[i]==color ) for ( int j=1;j<=n;j++ ) if ( c[j]==color ) pos[i][j][0]=pos[i][j][1]=pos[i][j][2]=0; 53 for ( int i=1;i<=m;i++ ) if ( c[u[i]]==color and c[v[i]]==color ) 54 for ( int j=1;j<=m;j++ ) if ( c[u[j]]==color and c[v[j]]==color ) if ( i!=j ) 55 { 56 if ( u[i]==u[j] ) Addedge(v[i],v[j],u[i]); 57 else if ( u[i]==v[j] ) Addedge(v[i],u[j],u[i]); 58 else if ( v[i]==u[j] ) Addedge(u[i],v[j],v[i]); 59 else if ( v[i]==v[j] ) Addedge(u[i],u[j],v[i]); 60 } 61 for ( int i=1;i<=m;i++ ) if ( c[u[i]]==color and c[v[i]]==color ) 62 for ( int j=1;j<=m;j++ ) if ( c[u[j]]==color and c[v[j]]==color ) 63 if ( i!=j ) if ( u[i]!=u[j] and u[i]!=v[j] and v[i]!=u[j] and v[i]!=v[j] ) 64 work(u[i],u[j],v[i],v[j]),work(u[i],v[j],v[i],u[j]),work(v[i],u[j],u[i],v[j]),work(v[i],v[j],u[i],u[j]); 65 } 66 signed main() 67 { 68 scanf("%d%d%d",&n,&m,&k); 69 for ( int i=0;i<=n;i++ ) for ( int j=0;j<=n;j++ ) dis[i][j]=-inf; 70 for ( int i=1;i<=m;i++ ) scanf("%d%d%lld",&u[i],&v[i],&w[i]),dis[u[i]][v[i]]=dis[v[i]][u[i]]=w[i]; 71 if ( k==3 ) 72 { 73 long long ans=-1; 74 for ( int i=1;i<=n-2;i++ ) for ( int j=i+1;j<=n-1;j++ ) for ( int k=j+1;k<=n;k++ ) ans=std::max(ans,dis[i][j]+dis[j][k]+dis[k][i]); 75 if ( ~ans ) printf("%lld\n",ans); 76 else puts("impossible"); 77 return 0; 78 } 79 srand(time(NULL));long long ans=-1; 80 for ( int Rnd=1;Rnd<=1000;Rnd++ ) 81 { 82 for ( int i=1;i<=n;i++ ) c[i]=rand()&1; 83 for ( int i=1;i<=n;i++ ) for ( int j=1;j<=n;j++ ) Dis[0][i][j]=Dis[1][i][j]=-inf; 84 for ( int i=1;i<=m;i++ ) if ( c[u[i]] and !c[v[i]] ) std::swap(u[i],v[i]); 85 if ( k==4 ) solve2(0),solve2(1); 86 else if ( k==5 ) solve2(0),solve3(1); 87 else if ( k==6 ) solve3(0),solve3(1); 88 else if ( k==7 ) solve3(0),solve4(1); 89 else if ( k==8 ) solve4(0),solve4(1); 90 else if ( k==9 ) solve4(0),solve5(1); 91 else if ( k==10 ) solve5(0),solve5(1); 92 for ( int i=1;i<=m;i++ ) if ( c[u[i]]^c[v[i]] ) for ( int j=1;j<=m;j++ ) if ( c[u[j]]^c[v[j]] ) 93 if ( i!=j ) ans=std::max(ans,Dis[0][u[i]][u[j]]+Dis[1][v[i]][v[j]]+w[i]+w[j]); 94 } 95 if ( ~ans ) printf("%lld\n",ans); 96 else puts("impossible"); 97 return 0; 98 }