743A:Vladik and flights
题意简述
有
n
个机场,编号为
在同家公司的机场间行动不耗代价,在不同公司的机场间移动,从
i
到
问从
x
到
数据范围
1≤n≤105
ai∈[0,1]
思路
水。
x,y
在同家公司答案为
0
,不同公司答案为
代码
#include<cstdio>
using namespace std;
int n,a,b;
char st[100010];
int main()
{
scanf("%d%d%d",&n,&a,&b);
scanf("%s",st+1);
if (st[a]==st[b])
printf("0");
else
printf("1");
return 0;
}
743B:Chloe and the sequence
题意简述
数列
a
初始只有一个元素
第
i
次操作将
求第
k
个元素是多少。
数据范围
1≤k≤2n−1
思路
经过观察可以发现。
答案即为
k
的二进制表示中最小的
代码
include<cstdio>
using namespace std;
int n;
long long k;
int main()
{
scanf("%d%I64d",&n,&k);
for (int i=0;i<=n;i++)
if (k&(1LL<<i))
{
printf("%d",i+1);
break;
}
return 0;
}
743C:Vladik and fractions
题意简述
给出
n
,问
数据范围
1≤n≤104
思路
这个题比较有意思…
首先
2n
可以拆成
1n+1n
然后瞬间联想到裂项项消……
1n−1n+1=1n(n+1)
于是答案就是
n,n+1,n(n+1)
一看
n
的范围,简直精致。
代码
#include<cstdio>
using namespace std;
int n;
int main()
{
scanf("%d",&n);
if (n==1)
printf("-1");
else
printf("%d %d %d\n",n,n+1,n*(n+1));
return 0;
}
743D:Chloe and pleasant prizes
题意简述
给出一棵
求两个不相交的子树,权值和的最大值。
如果没有这样的方案,输出
−1
数据范围
2≤n≤105
−109≤ai≤109
思路
树形DP。
DP出
mxi
数组,表示以
i
为根的子树中,最大的子树权值和。
以一个节点为根的答案一定是它所有孩子的
所有的答案扫一遍就行了。
注意一条链的情况无解。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#define INF 1LL<<60
struct edge{
int s,t,next;
}e[400010];
int head[200010],cnt;
void addedge(int s,int t)
{
e[cnt].s=s;e[cnt].t=t;e[cnt].next=head[s];head[s]=cnt++;
e[cnt].s=t;e[cnt].t=s;e[cnt].next=head[t];head[t]=cnt++;
}
int n,u,v;
long long sum[200010],mx[200010];
int size[200010],val[200010];
long long ans=-INF,se;
void dfs(int node,int lastfa)
{
sum[node]=val[node];
size[node]=0;
for (int i=head[node];i!=-1;i=e[i].next)
if (e[i].t!=lastfa)
{
dfs(e[i].t,node);
sum[node]+=sum[e[i].t];
size[node]++;
}
mx[node]=-INF;
se=-INF;
for (int i=head[node];i!=-1;i=e[i].next)
if (e[i].t!=lastfa)
{
if (mx[e[i].t]>mx[node])
{
se=mx[node];
mx[node]=mx[e[i].t];
}
else if (mx[e[i].t]>se)
se=mx[e[i].t];
}
if (size[node]>1)
ans=max(ans,mx[node]+se);
mx[node]=max(mx[node],sum[node]);
}
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",&val[i]);
memset(head,0xff,sizeof(head));
cnt=0;
for (int i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
addedge(u,v);
}
dfs(1,1);
if (ans==-INF)
printf("Impossible");
else
printf("%I64d",ans);
return 0;
}
743E:Vladik and cards
题意简述
给出
n
个元素的一个序列,序列中所有元素
问最长的满足如下条件的子序列长度:
1.子序列中所有相同元素相邻。
2.子序列中任意两种元素的个数之差的绝对值
≤1
数据范围
1≤n≤1000
ai∈[1,8]
思路
二分+DP。
我们可以发现,所有元素的出现次数之可能为
k
或
k
对于答案的影响是单调的。
所有我们可以二分这个
如何DP?
首先预处理。
pos[i][j]
表示
i
元素第
ord[i]
表示原序列的第
i
个的那种元素是第几次出现。
DP数组
f[i][mask]
只可能有三种状态转移:
1.
i
不取。
2.取
3.取
据此转移就可以了。
时间复杂度
O(28nlog(n8))
代码
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
int f[1010][260];
int n,lim,l,r,mid,tmp,ans,now;
int seq[1010],ord[1010],cnt[10],pos[10][1010];
bool judge(int x)
{
tmp=0;
memset(f,0xef,sizeof(f));
f[0][0]=0;
for (int i=1;i<=n;i++)
for (int j=0;j<=lim;j++)
{
f[i][j]=f[i-1][j];
if (j&(1<<seq[i]))
{
now=ord[i];
if (x>0&&now-x+1>=1)
f[i][j]=max(f[i][j],f[pos[seq[i]][now-x+1]-1][j^(1<<seq[i])]+x);
if (now-x>=1)
f[i][j]=max(f[i][j],f[pos[seq[i]][now-x]-1][j^(1<<seq[i])]+x+1);
}
}
if (x==0)
for (int i=0;i<=lim;i++)
tmp=max(tmp,f[n][i]);
else
tmp=f[n][lim];
return tmp>0;
}
int main()
{
scanf("%d",&n);
lim=(1<<8)-1;
for (int i=1;i<=n;i++)
{
scanf("%d",&seq[i]);
seq[i]--;
}
for (int i=1;i<=n;i++)
{
ord[i]=++cnt[seq[i]];
pos[seq[i]][cnt[seq[i]]]=i;
}
l=0,r=n/8;
while (l<=r)
{
mid=(l+r)>>1;
if (judge(mid))
ans=tmp,l=mid+1;
else
r=mid-1;
}
printf("%d",ans);
return 0;
}