HGOI 20181030晚 题解
Problem:给出全班人的个数总分和小明的分数(满分100分),求小明最低排名和最高排名
sol:假设小明的排名为k,总分为sum,小明的分数是r,
贪心求解,
最坏情况下,小明前面的比小明高一分(r+1)分,那么给后面的人的分数是sum-(k-1)*(r+1)-r
最优情况下,小明前面的人都是满分(100)分,那么给后面人的分数是sum-(k-1)*100-r
所以对于后面人分数的取值范围是A=[sum-(k-1)*100-r,sum-(k-1)*(r+1)-r]
而对于一个很显然的结论由于小明是第k名所以在他后面(含一样的分数)有n-k人,他们的分数的取值范围是B=[0,(n-k)*r]
如果说A和B有交集的话该问题有解,可以求出连续的一段范围求最小的l,最大的r
判断交集快速的方法是
1. 判包含关系。
2.判某条线段的左右断点是不是在另外一条线段内。
# include<bits/stdc++.h> using namespace std; bool check(int l1,int r1,int l2,int r2) { if (l1>r1) swap(l1,r1); if (l2>r2) swap(l2,r2); if (l1>=l2&&r2>=r1) return 1; if (l2>=l1&&r1>=r2) return 1; if (l2>=l1&&l2<=r1) return 1; if (r2>=l1&&r2<=r1) return 1; return 0; } int main() { int N,A,S; scanf("%d%d%d",&N,&A,&S); if (A==100) { puts("1 1");return 0; } int l=N,r=0; for (int i=1;i<=N;i++) if (check(S-(i-1)*(A+1)-A,S-(i-1)*100-A,0,A*(N-i))) { l=min(l,i); r=max(r,i); } printf("%d %d\n",l,r); return 0; }
sol:考虑问题平方和就是要大的数尽可能大,通过题目中所说的东西
- rec1=a or b
- rec2=a and b
- a=rec1 ,b=rec2
发现本质其实是把两个数的某个二进制位交换,所以最后就是每位有若干的1,让你分配到对应的位置,最大化平方和
就是尽可能放大的数,然后平方以后就是最大的。
# include <bits/stdc++.h> # define int long long using namespace std; const int MAXN=21; int b[MAXN],n; inline int read() { int X=0,w=0; char c=0; while (!(c>='0'&&c<='9')) w|=c=='-',c=getchar(); while (c>='0'&&c<='9') X=(X<<1)+(X<<3)+(c^48),c=getchar(); return w?-X:X; } void getbit(int x) { for (int i=0;i<=20;i++) if ((x>>i)&1==1) b[i]++; } signed main() { n=read(); int x; for (int i=1;i<=n;i++) x=read(),getbit(x); int num=0;for (int i=0;i<=20;i++) num+=b[i]; int ans=0; while (1) { if (num==0) break; int tmp=0; for (int i=20;i>=0;i--) if (b[i]>0) b[i]--,tmp+=(1<<i),num--; ans+=tmp*tmp; } cout<<ans<<endl; return 0; }
sol:一道DP题其实比较简单
一开始状态定义错了,导致最长不下降子序列弄错了(其实和前面0的个数有关系)
f[i][j][k]前i个,有j个0,LIS长度为k个的数的可能性
若第i+1位为0,那么f[i+1][j+1][k]+=f[i][j][k]
若第i+1位为1,那么f[i+1][j][max(k,j)]+=f[i][j][k]
为什么是max(k,j)呢? 由于第i+1位为是1那么和前面有j个0构成一个最长上升子序列长度是j,
而此时加入一个1才到达长度为k,那么此时最长的长度可能并不会发生更新,可能还是前面都是0的子序列更优(一开始没有把0的个数作为一个状态!)
初始值f[0][0][0]=1
# include <bits/stdc++.h> # define int long long using namespace std; const int MAXN=205; const int mo=1e9+7; int n; int f[MAXN][MAXN][MAXN]; inline int read() { int X=0,w=0; char c=0; while (!(c>='0'&&c<='9')) w|=c=='-',c=getchar(); while (c>='0'&&c<='9') X=(X<<1)+(X<<3)+(c^48),c=getchar(); return w?-X:X; } signed main() { n=read(); memset(f,0,sizeof(f)); f[0][0][0]=1; //f[i][j][k]前i个,有j个0,长度为k个数 for (int i=0;i<=n;i++) for (int j=0;j<=n;j++) for (int k=0;k<=n;k++) { f[i+1][j+1][k]=(f[i+1][j+1][k]+f[i][j][k])%mo; f[i+1][j][max(k,j)+1]=(f[i+1][j][max(k,j)+1]+f[i][j][k])%mo; } int ans=0; for (int j=0;j<=n;j++) for (int k=0;k<=n;k++) ans=(ans+(max(k,j)*f[n][j][k])%mo)%mo; cout<<ans<<endl; return 0; }