A:Vladik and flights
题目大意:给定一个长度为n的01串和a,b两个位置,如果一个位置和另一个位置上的数相同,那么这两个位置之间相互到达的代价是0,否则代价是这两个位置的距离,问从a到b最少需要多少代价。
思路:答案非0即1,0的情况就是这两个位置上的数相同,1就代表一定同时存在0和1,那么就把这两个点分别移到0,1交界的位置,那么答案就是1。
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 100005
const int inf=1e9;
int n,st,ed,ans;
char s[maxn];
inline int read(){
int x=0,f=1;char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*f;
}
inline void print(int x){
if (x>=10) print(x/10);putchar(x%10+'0');
}
int main(){
n=read(),st=read(),ed=read();
scanf("%s",s+1);
puts(s[st]==s[ed]?"0":"1");
return 0;
}
B:Chloe and the sequence
题目大意:给定一个序列的构造方式,第一个序列是[1],第二个序列是[1,2,1],第三个序列是[1,2,1,3,1,2,1],然后第n个序列就是把第n-1个序列翻折后再在最中间加一个数n,然后问第n个序列中第k个数是多少。
思路:设len为第n个序列的长度(显然len=(1<<n)-1),mid为最中间数的位置,然后如果k>mid,显然有a[k]=a[len-k+1](满足对称性),然后如果k<mid,那么就可以把序列折半看成是一个子问题,k==mid就输出这是第几个序列就好了。
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n;
long long len,k;
inline long long read(){
long long x=0,f=1;char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*f;
}
inline void print(int x){
if (x>=10) print(x/10);putchar(x%10+'0');
}
int main(){
n=read(),k=read();
len=(1ll<<n)-1;
for (int i=n;i;i--){
if (k==len/2+1){printf("%d\n",i);return 0;}
if (k>len/2+1) k=len-k+1;
len>>=1;
}
return 0;
}
C:Vladik and fractions
题目大意:给定n,找到三个互不相同的整数使得2/n=1/a+1/b+1/c。
思路:2/n=1/n+1/(n+1)+1/(n*(n+1)),注意互不相同要特判n=1的情况。
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n,m;
inline int read(){
int x=0,f=1;char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*f;
}
inline void print(int x){
if (x>=10) print(x/10);putchar(x%10+'0');
}
int main(){
n=read();
if (n==1){puts("-1");return 0;}
else{printf("%d %d %d\n",n,n+1,n*(n+1));}
return 0;
}
D:Chloe and pleasant prizes
题目大意:一颗以1为根的树,给定每个点的点权,找到两颗没有交集的子树并使它们的权值和尽量大,如果找不到输出-1。
思路:首先找不到的情况就是一条链的情况(当然1必须是该链的一个端点),然后答案直接就tree dp就好了,和tree dp求树最长链基本一毛一样。
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 200005
const long long inf=1e18;
int n,m,maxdeg,tot;
long long ans;
int now[maxn],pre[maxn*2],son[maxn*2],deg[maxn];
long long sum[maxn];
bool vis[maxn];
inline int read(){
int x=0,f=1;char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*f;
}
inline void print(int x){
if (x>=10) print(x/10);putchar(x%10+'0');
}
void add(int a,int b){
son[++tot]=b;
pre[tot]=now[a];
now[a]=tot;
}
void link(int a,int b){
add(a,b),add(b,a);
}
long long tree_dp(int x,int fa){
long long mx=-inf,tx=-inf;
for (int p=now[x];p;p=pre[p])
if (son[p]!=fa){
long long tmp=tree_dp(son[p],x);
if (tmp>mx) tx=mx,mx=tmp;
else tx=max(tx,tmp);
ans=max(ans,tx+mx);
sum[x]+=sum[son[p]];
}
return max(mx,sum[x]);
}
void dfs(int x,int fa){
vis[x]=1;
for (int p=now[x];p;p=pre[p]) if (son[p]!=fa){dfs(son[p],x);break;}
}
bool check(){
dfs(1,0);
for (int i=1;i<=n;i++) if (!vis[i]) return 1;
return 0;
}
int main(){
n=read(); ans=-inf;
for (int i=1;i<=n;i++) sum[i]=read();
for (int i=1,a,b;i<n;i++) a=read(),b=read(),link(a,b);
if (!check()){puts("Impossible"); return 0;}
tree_dp(1,0);
printf("%lld\n",ans);
return 0;
}
E:Vladik and cards
题目大意:在给定的只由1到8组成的序列中,找到一个子序列,使得其满足以下条件:1.任意一个数字出现的次数之间不能相差超过1,也就是说每个数字要么出现x次,要么出现x+1次。2.如果子序列中第一次出现某个数字,就必须全部出现完,也就是说相同的数字在子序列中一定是一条线段而不能断开。在以上基础上要求子序列长度最长。
思路:状压dp很显然,关键是二分比较难想,因为一个数字要么出现x次,要么出现x+1次,那么我们就二分这个x(显然具有单调性),然后考虑如何去check就好了。
因为答案肯定是形如x*y+(8-y)*(x+1),这个y就是有多少个数字出现x次,然后就可以令状态f[i][S]表示当前状态为S,当前位于第i个时的y的值,然后枚举当前选哪个数字转移即可。
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 1005
#define maxs (1<<8)
const int inf=1e9;
int n,ans=-inf;
int a[maxn],cnt[9];
int f[maxn][maxs];
inline int read(){
int x=0,f=1;char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar()) if (ch=='-') f=-1;
for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*f;
}
vector<int> v[9];
void clear(){memset(cnt,0,sizeof(cnt)),memset(f,-1,sizeof(f));}
bool check(int mid){
clear();f[1][0]=0;
for (int i=1;i<=n;i++){
for (int S=0;S<maxs;S++){
if (f[i][S]==-1) continue;
for (int j=1;j<=8;j++)
if (!(S&(1<<(j-1)))){
int x=cnt[j]+mid;
if (x>v[j].size()) continue;
f[v[j][x-1]+1][S|(1<<(j-1))]=max(f[v[j][x-1]+1][S|(1<<(j-1))],f[i][S]);
if (++x>v[j].size()) continue;
f[v[j][x-1]+1][S|(1<<(j-1))]=max(f[v[j][x-1]+1][S|(1<<(j-1))],f[i][S]+1);
}
}
cnt[a[i]]++;
}
int tt=-1;
for (int i=1;i<=n+1;i++) tt=max(tt,f[i][maxs-1]);
return tt==-1?0:(ans=max(ans,tt*(mid+1)+(8-tt)*mid),1);
}
int main(){
n=read();
for (int i=1;i<=n;i++) a[i]=read(),v[a[i]].push_back(i);
int l=1,r=n;
while (l<=r){
int mid=(l+r)>>1;
if (check(mid)) l=mid+1;
else r=mid-1;
}
if (ans==-inf){
ans=0;
for (int i=1;i<=8;i++) if (v[i].size()) ans++;
}
printf("%d\n",ans);
return 0;
}