这次周赛的后两题挺有意思的,但是我一个也没写出来,哭 记录一下解法
签到
1 2 3 4 5 6 7 8 9 10 class Solution {public : double trimMean (vector <int >& arr) { sort(arr.begin(),arr.end()); double ans=0 ; int t=arr.size()/20 ; for (int i=t;i<arr.size()-t;i++)ans+=arr[i]; return ans/(arr.size()-2 *t); } };
暴力枚举被覆盖的所有点,对这些点按照信号大小和位置排序就可以了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class Solution {public : struct Node { int x,y,sig; Node(){} Node(int xx,int yy,int ss){ x=xx;y=yy;sig=ss; } bool operator < (const Node nd)const { if (sig==nd.sig){ if (x==nd.x)return y<nd.y; else return x<nd.x; } else return sig>nd.sig; } }; double dis (double x1,double y1,double x2,double y2) { return sqrt ((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)); } vector <int > bestCoordinate (vector <vector <int >>& towers, int r) { vector <Node>nd; for (int i=-r;i<=50 +r;i++){ for (int j=-r;j<=50 +r;j++){ int sum=0 ; for (int k=0 ;k<towers.size();k++){ if (dis(i,j,towers[k][0 ],towers[k][1 ])<=r){ sum+=towers[k][2 ]/(dis(i,j,towers[k][0 ],towers[k][1 ])+1 ); } } nd.push_back(Node(i,j,sum)); } } sort(nd.begin(),nd.end()); return {nd[0 ].x,nd[0 ].y}; } };
这题我一开始用dp[i][j]表示k个线段覆盖n个点的方案数,结果到最后也没有找到转移方程,赛后才发现状态表示少了一个维度。正确的状态定义如下
f[n][k]表示用k条线段覆盖0,1,…n这些点,并且点n没有被第k条线段的右端点覆盖的方案数
g[n][k]表示用k条线段覆盖n个点,且点n被第k条线段的右端点覆盖了的方案数。
对于f[i][j],由于第i个点没有被覆盖,因此它应该等于前i-1个点被k条线段覆盖的方案数,即
而对于g[i][j],可分为两种情况:
最后的答案即为f[n-1][k]+g[n-1][k]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution { static int mod=1000000007 ; public int numberOfSets (int n, int k) { int f[][]=new int [n+1 ][k+1 ]; int g[][]=new int [n+1 ][k+1 ]; f[0 ][0 ]=1 ; for (int i=1 ;i<=n;i++){ for (int j=0 ;j<=Math.min(k,i);j++){ f[i][j]=(f[i-1 ][j]+g[i-1 ][j])%mod; g[i][j]=g[i-1 ][j]; if (j>0 ){ g[i][j]=(g[i][j]+f[i-1 ][j-1 ])%mod; g[i][j]=(g[i][j]+g[i-1 ][j-1 ])%mod; } } } return f[n][k]; } }
另外还有一种神奇的组合数学解法:
题目等价于求满足
的$(l_1,\cdots,l_k,r_1,\cdots,r_k)$的方案数,令$l’_i = l_i + (i-1),r’_i = r_i + (i-1)$,那么得到的$(l’_1,\cdots,l’_k,r’_1,\cdots,r’_k)$与$(l_1,\cdots,l_k,r_1,\cdots,r_k)$是逐一对应的,并且满足
由于$(l’_1,\cdots,l’_k,r’_1,\cdots,r’_k)$与$(l_1,\cdots,l_k,r_1,\cdots,r_k)$可以一一对应,所以他们的方案数也应该是相同的,所以答案等于在n+k-1个数中任选$2k$个的方案数,最终答案为
这题用线段树是可以做的,但是由于每次更新操作都是对当前的整个序列(对于最终的序列a来说,则是每次对a的前缀)进行操作,所以可以用更简洁的做法来解决。
具体做法是维护两个标记add和mul。当序列需要进行更新操作时,并不对整个序列做更新,而是只更新这两个标记(类似线段树的lazy标记),当遇到对a[i]的查询操作时,返回值为a[i]*mul+add。
但是这样还有一个问题:当一个新的元素x插入进来时,如果直接将他存到数组a中,查询时会得到错误的结果,因为add和mul中还记录了x插入之前的更新信息。因此,为了保持这两个标记对这个新元素x的正确性,我们需要在插入之前先对x先做一下逆操作,即令
然后将y存入数组a中,这样在查询的时候我们才能得到正确的x。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 class Fancy { List<Long> a; long add=0 ,mul=1 ; long mod=1000000007 ; long qpow (long a,long b,long mod) { long ans=1 ; while (b>0 ){ if ((b&1 )>0 )ans=ans*a%mod; a=a*a%mod; b>>=1 ; } return ans; } public Fancy () { a=new ArrayList(); } public void append (int v) { long t=qpow(mul,mod-2 ,mod); long val=v; val=((val-add)%mod+mod)%mod; val=val*t%mod; a.add(val); } public void addAll (int inc) { add=(add+inc)%mod; } public void multAll (int m) { add=add*m%mod; mul=mul*m%mod; } public int getIndex (int idx) { if (idx>=a.size())return -1 ; return (int )((a.get(idx)*mul+add)%mod); } }