传送门:cf1088
C.Ehab and a 2-operation task
n
n
n次加+1次取模,满足最后
a
i
=
i
a_i=i
ai=i。
每个数最少需要加的次数是
i
−
a
i
(
m
o
d
n
+
1
)
i-a_i\pmod {n+1}
i−ai(modn+1),由于要加的值必须单调不增,所以倒着处理每个数需要加的值
n
d
i
nd_i
ndi,若
n
d
i
<
n
d
i
+
1
nd_i<nd_{i+1}
ndi<ndi+1,就
n
d
i
+
=
(
n
+
1
)
nd_i+=(n+1)
ndi+=(n+1)。最后模
n
+
1
n+1
n+1即可。
#include<bits/stdc++.h>
using namespace std;
const int N=2005;
int n,a[N],nd[N],bs=0;
int op[N],u[N],v[N],cnt;
int main(){
int i,j;
scanf("%d",&n);
for(i=1;i<=n;++i) scanf("%d",&a[i]);
for(i=n;i;--i){
nd[i]=bs+i-a[i];
for(;nd[i]<0 || nd[i]<nd[i+1];){
bs+=(n+1);nd[i]+=(n+1);
}
}
for(i=n;i;--i){
if(nd[i]>nd[i+1]){
op[++cnt]=1;u[cnt]=i;
v[cnt]=nd[i]-nd[i+1];
}
}
op[++cnt]=2;u[cnt]=n;v[cnt]=n+1;
printf("%d\n",cnt);
for(i=1;i<=cnt;++i) printf("%d %d %d\n",op[i],u[i],v[i]);
return 0;
}
D.Ehab and another another xor problem
倒着从大到小逐位考虑,得到高位的准确值后,相当于每次都在询问最高位。
可以对于第 i i i位,可以分别考虑 a , b a,b a,b这一位上为 ( 1 , 1 ) , ( 0 , 0 ) , ( 1 , 0 ) . ( 0 , 1 ) (1,1),(0,0),(1,0).(0,1) (1,1),(0,0),(1,0).(0,1)的情况。
但注意光考虑 b c bc bc这一位为 ( 1 , 1 ) , ( 0 , 0 ) , ( 1 , 0 ) . ( 0 , 1 ) (1,1),(0,0),(1,0).(0,1) (1,1),(0,0),(1,0).(0,1)四种情况中的任意两种都是无法推出唯一的 a , b a,b a,b确定值的(可以自己写一下),还需要知道 a , b a,b a,b去掉最高位后的大小。
但每位最多询问2次,而这2次中必然有一次包含了 a , b a,b a,b去掉最高位后的大小关系。
于是可以先记录 a , b a,b a,b的大小关系,逐步递推去掉最高位后的大小关系,答案就是唯一的了。
#include<bits/stdc++.h>
using namespace std;
const int N=2005;
int n,a,b,v,mw;
int main(){
int i,j,x,y;
cout<<"? "<<a<<" "<<b<<endl;
scanf("%d",&mw);
for(i=29;~i;--i){
j=(1<<i);
cout<<"? "<<a<<" "<<(b|j)<<endl;
scanf("%d",&x);
cout<<"? "<<(a|j)<<" "<<(b|j)<<endl;
scanf("%d",&y);
if((!x)){
if(y==-1) a|=j;
else b|=j;
mw=0;
}else if((!y)){
if(x==1) a|=j,b|=j;
mw=0;
}else if((x==1)&&(y==-1)){
if(mw==1) a|=j;
else a|=j,b|=j,mw=-1;
}else if((x==-1)&&(y==1)){
if(mw==-1) b|=j;
}else if((x==1)&&(y==1)){
if(mw==-1) mw=1,b|=j;
else mw=1,a|=j,b|=j;
}else{
if(mw==1) mw=-1,a|=j;
else mw=-1;
}
}
cout<<"! "<<a<<" "<<b<<endl;
return 0;
}
E.Ehab and a component choosing problem
首先不考虑 k k k,只贪心让答案值尽量大: d p i dp_{i} dpi表示 i i i所在的联通块最大值,一遍 d f s dfs dfs求得最大值 a n s = m a x ( d p i ) ans=max(dp_i) ans=max(dpi)。
注意到再把这个最大的连通块分成多块,或者取其它值更小的联通块,答案显然不是最优的。
那么再贪心取出最多的值为 a n s ans ans的不相交连通块即可(贪心向下取)。
std:
#include <iostream>
#include <vector>
using namespace std;
int a[300005],k;
long long dp[300005],ans=-1e9;
vector<int> v[300005];
void dfs(int node,int p,bool f)
{
dp[node]=a[node];
for (int u:v[node])
{
if (u!=p)
{
dfs(u,node,f);
dp[node]+=max(dp[u],0LL);
}
}
if (f)
ans=max(ans,dp[node]);
else if (dp[node]==ans)
{
dp[node]=0;
k++;
}
}
int main()
{
int n;
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
for (int i=1;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
v[a].push_back(b);
v[b].push_back(a);
}
dfs(1,0,1);
dfs(1,0,0);
printf("%I64d %d",ans*k,k);
}
F. Ehab and a weird weight formula
将点对边的贡献转成点对 w w w的贡献:
类似于MST的做法,考虑每次加入一个点 i i i(连向以选中集合中的一点 j j j),贡献为 a i + a j ( 1 + ⌈ log 2 d i s ( i , j ) ⌉ ) a_i+a_j(1+\lceil\log_2dis(i,j)\rceil) ai+aj(1+⌈log2dis(i,j)⌉)。称这条边为 i i i的所属边(值最小的点没有所属边)。最小化 a j ( 1 + ⌈ log 2 d i s ( i , j ) ⌉ ) a_j(1+\lceil\log_2dis(i,j)\rceil) aj(1+⌈log2dis(i,j)⌉)即可。
那么构造出的这颗树必然满足父亲结点的值 ≤ \leq ≤儿子结点的值。
于是以值最小的点为根向下
d
f
s
dfs
dfs,维护每个点
2
k
2^k
2k级祖先,存在这样一个性质:每个点的所属边必然只可能连向它的某个
2
k
2^k
2k级祖先或者根节点。
因为若它的所属边不连向根,而
a
r
o
o
t
<
a
i
(
i
≠
r
o
o
t
)
a_root<a_i(i\neq root)
aroot<ai(i̸=root),则它必然连向了更近的点,且这个点在
⌈
log
2
d
i
s
(
i
,
j
)
⌉
\lceil\log_2dis(i,j)\rceil
⌈log2dis(i,j)⌉相等的情况下要尽量小(深度尽量小)。
复杂度 O ( n l o g n ) O(nlogn) O(nlogn)
std:
#include <iostream>
#include <string.h>
#include <vector>
using namespace std;
vector<int> v[500005];
int m=1,a[500005],dp[20][500005];
long long ans;
void dfs(int node,int p)
{
dp[0][node]=p;
for (int i=1;i<20;i++)
{
if (dp[i-1][node]!=-1)
dp[i][node]=dp[i-1][dp[i-1][node]];
}
int d;
long long mn=(1LL<<60);
for (d=0;d<20 && dp[d][node]!=-1;d++)
mn=min(mn,(long long)(d+1)*a[dp[d][node]]+a[node]);
mn=min(mn,(long long)(d+1)*a[m]+a[node]);
if (p!=-1)
ans+=mn;
for (int u:v[node])
{
if (u!=p)
dfs(u,node);
}
}
int main()
{
int n;
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if (a[i]<a[m])
m=i;
}
for (int i=1;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
v[a].push_back(b);
v[b].push_back(a);
}
memset(dp,-1,sizeof(dp));
dfs(m,-1);
printf("%I64d",ans);
}