C. Ehab and a 2-operation task
题意:给一个长度为n的数列,你有两种操作 :1 i x 表示从a1到ai每个数都加x,2 i x 表示从a1到ai每个数都模等于x,你至多有n+1次操作,求怎么把原数列构造成一个严格递增的数列。
思路:我们先把所有数都加上一个足够大的数(远大于n),假设前面 i 个数我已经构造成 1 2 3.... i 的数列,又因为后面的数远大于n,所以我只要把前 i+1个数全部模a[ i+1 ]-(i+1),那么第i+1个数就变成了 i+1,而且这次取模操作不影响前面 i 个数。
#include<cstdio>
using namespace std;
int a[10000];
int main()
{
int n,k=100000;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),a[i]+=k;
printf("%d\n",n+1);
printf("1 %d %d\n",n,k);
for(int i=1;i<=n;i++)
printf("2 %d %d\n",i,a[i]-i);
}
D. Ehab and another another xor problem
题意:你要求两个数 a b,你可以向系统问一个问题: ? c d,系统返回1 表示 a^c>b^d,返回-1则表示小于,返回0表示等于,已知a和b小于 1<<30,要求在至多62次询问求出a和b的值。
思路:这个题相当有意思,我们用二进制来看待这两个数,假设我已经知道了a和b的第i+1位到29位,并把已知的信息全部异或0得到c,d,我怎么求a和b的第 i 位了,我先dfs问第i位 ? c d,如果返回1,那么a和b有这三种情况:(xxx1xxx,xxx0xxx),(xxx1xxx,xxx1xxx),(xxx0xxx,xxx0xxx),显然我需要再到判断函数去判断,判断函数我再问 ? c|(1<<i) d|(1<<i),如果返回-1,说明a,b是(xxx1xxx,xxx0xxx),然后进行下一次dfs查找第i-1位,如果是1,麻烦了有两种情况,还要问一次,那我再问一次 ? c|(1<<i) d,就可以判断a和b的第 i 位是都为1 还是都为0了,不过我为了查询第 i 位用了3次操作,3*30>62,看起来要gg,其实没关系,因为我已经知道了,a和b的第i-1位的具体情况了:(xxxx1xx,xxxx0xx),(xxxx1xx,xxxx1xx),(xxxx0xx,xxxx0xx),求第i-1位我可以省下一个步骤,求到最后的 c d的答案就是a和b,我只推导到这,剩下的就靠你来ac了。
#include<cstdio>
#include<iostream>
using namespace std;
int a=0,b=0,x,i=29;
void dfs2();
void dfs()
{
if(i<0)return;
printf("? %d %d\n",a,b);
cin>>x;
dfs2();
}
void dfs2()
{
if(i<0)return;
if(x==1)
{
printf("? %d %d\n",a|(1<<i),b|(1<<i));
cin>>x;
if(x==-1)
{
a|=(1<<i);
i--;
dfs();
}
else
{
printf("? %d %d\n",a|(1<<i),b);
cin>>x;
if(x==-1)
a|=(1<<i),b|=(1<<i);
i--;
x=1;
dfs2();
}
}
else if(x==-1)
{
printf("? %d %d\n",a|(1<<i),b|(1<<i));
cin>>x;
if(x==1)
{
b|=(1<<i);
i--;
dfs();
}
else
{
printf("? %d %d\n",a|(1<<i),b);
cin>>x;
if(x==-1)
a|=(1<<i),b|=(1<<i);
i--;
x=-1;
dfs2();
}
}
else
{
printf("? %d %d\n",a|(1<<i),b);
cin>>x;
if(x==-1)
a|=(1<<i),b|=(1<<i);
i--;
dfs();
}
}
int main()
{
dfs();
printf("! %d %d\n",a,b);
}
E. Ehab and a component choosing problem
题意:给你一棵树,每个节点有权值,你可以选择k个集合(每个节点只能属于一个集合,且集合内的点必须是联通的),使得所有集合的总权值/k最大,在满足这个条件的前提下,k也要最大。
思路:这真是个水题,比D题不知道简单多少.....首先我知道只选择一个集合肯定可以求出最大的ans,那么我首先解决ans,再解决k,设d[ i ]为以 i 为根节点的子树所构成的集合的最大权值。显然d[ i ]=max(d[ i ],d[ i ]+d[ son ]),通过此方法求出ans后,第二次dfs找到有几个d[ i ]=ans就可以了。
#include<cstdio>
#include<vector>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn=3e5+10,inf=1e9;
typedef long long ll;
int a[maxn],n,k=0;
ll d[maxn],ans=-inf;
vector<int>G[maxn];
void dfs(int u,int fa)
{
d[u]=a[u];
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i];
if(v==fa)continue;
dfs(v,u);
d[u]=max(d[u],d[u]+d[v]);
}
ans=max(ans,d[u]);
}
void dfs2(int u,int fa)
{
d[u]=a[u];
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i];
if(v==fa)continue;
dfs2(v,u);
d[u]=max(d[u],d[u]+d[v]);
}
if(d[u]==ans)
d[u]=-inf,k++;
}
int main()
{
int u,v;
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(1,-1);
dfs2(1,-1);
cout<<ans*k<<' '<<k;
}
F. Ehab and a weird weight formula
题意:给你n个点的权值ai,一棵树,定义dist(a,b)为这棵树上两点的距离,且除了唯一的一个权值最小的点,每个点都必有一个相邻点权值比其小,要你构造一颗新的树,规定每加一条边u<-->v,w就加au+av+log2( dist(u,v) )* min( au , av )。求出最小的w。
思路: wa007
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=5e5+5;
typedef long long ll;
int f[maxn][20],a[maxn];
vector<int>G[maxn];
ll ans=0,inf=1e18;
void dfs(int u,int fa)
{
f[u][0]=fa;
for(int i=1;i<20;i++)
f[u][i]=f[f[u][i-1]][i-1];
ll res=inf;
for(int i=0;i<20;i++)
res=min(res,(i+1ll)*a[f[u][i]]+a[u]);
if(fa!=u)
ans+=res;
for(int i=0;i<G[u].size();i++)
if(G[u][i]!=fa)
dfs(G[u][i],u);
}
int main()
{
int n,u,v,rt=1;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(a[i]<a[rt])
rt=i;
}
for(int i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(rt,rt);
printf("%I64d\n",ans);
}