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 ",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+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 ",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 ",ans); return 0; }