Pro
一套暴力都无从下手的题目
Sol
susume
结论:在最优的方案中,青蛙一定会在某次恰好跳到一个禁区的右端点。
错解:二分……
因为是SPJ,没法测我自己的程序,不过0.2s的时限也是真的够了……挂标程
//#line 7 "TheFrog.cpp"
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <climits>
#include <algorithm>
#include <vector>
#include <string>
#include <iostream>
#include <map>
#include <queue>
#include <utility>
#include <ctime>
#include <sstream>
using namespace std;
typedef long long ll;
#define pair(x, y) make_pair(x, y)
#define N 50
class TheFrog {
public:
static const double eps = 1e-9;
pair<double, double> p[N + 1];
int n, D_;
double ans, m;
inline bool check(double x) {
for (int i = 1; i <= n; ++i) {
int ds = (int)(p[i].first / x + eps);
if (x * (double)(ds + 1) < p[i].second - eps)
return false;
}
return true;
}
double getMinimum(int D, vector <int> L, vector <int> R) {
ans = 1e10, m = 1e10, D_ = D;
n = L.size();
for (int i = 1; i <= n; ++i) p[i] = pair((double)L[i - 1], (double)R[i - 1]);
for (int i = 0; i < n; ++i) m = min(m, (double)R[i] - L[i]);
sort(p + 1, p + n + 1);
for (int i = 1; i <= n; ++i) {
double x = p[i].second;
int div = (int)(x / ans);
while (x / (double)div > m - eps) {
if (check(x / (double)div)) ans = min(ans, x / (double)div);
++div;
}
}
return ans;
}
};
int n, D;
vector <int> L, R;
int main() {
freopen("susume.in", "r", stdin);
freopen("susume.out", "w", stdout);
TheFrog solution;
scanf("%d%d", &n, &D);
for (int i = 1; i <= n; ++i) {
int l, r;
scanf("%d%d", &l, &r);
L.push_back(l), R.push_back(r);
}
double ans = solution.getMinimum(D, L, R);
printf("%.10lf\n", ans);
fclose(stdin);
fclose(stdout);
return 0;
}
setdiff
又考了组合数……
枚举所有子集进行统计可以拿到30分。
最大值和最小值没有关系,可以分开计算。下面的分析中只考虑最大值。
考虑枚举成为最大值的元素,此时可以与这个元素在同一个集合里的应该是小于它的其他元素。
对于等于它的其它元素,为了避免重复计算,我们认为只有位置在其左侧的可以加入。
假设此时有个元素可以加入,那么方案数就是 C i − 1 k − 1 C_{i-1}^{k-1} Ci−1k−1,因为这个元素本身必须加入。
那包含这个元素的所有集合对答案的贡献为:(元素值) × C i − 1 k − 1 ×C_{i-1}^{k-1} ×Ci−1k−1。
朴素的做法可以达到的复杂度 O ( n 2 ) O(n^2) O(n2),此时可以用Pascal公式处理组合数。可以拿到60分。
注意到,原始序列(序列)的顺序与答案无关。因此先对序列a排序。
再考虑成为最大值的元素。此时能够加入的元素即为其左侧的所有元素。
此时的复杂度为 O ( n l o g n ) O(nlogn) O(nlogn)。因此考虑用预处理阶乘和其逆元的方法求组合数。可以拿到100分。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
long long n , k , mod , num[100005] , jc[100005] , ans;
inline long long mypow(long long a , long long b) {
if(!b)
return 1;
long long t = mypow(a , b>>1)%mod;
if(b&1)
return t*t%mod*a%mod;
return t*t%mod;
}
inline long long inv(long long x) {
return mypow(x , mod-2)%mod;
}
inline void init() {
jc[1] = 1;
for(int i=2; i<=n; i++)
jc[i] = jc[i-1]*i%mod;
}
inline long long C(long long x , long long y) {
if(x<y)
return 0;
if(x==y)
return 1;
return jc[x]*inv(jc[y])%mod*inv(jc[x-y])%mod;
}
bool cmp1(long long a , long long b) { return a<b; }
bool cmp2(long long a , long long b) { return a>b; }
int main() {
freopen("setdiff.in","r",stdin);
freopen("setdiff.out","w",stdout);
scanf("%lld%lld%lld",&n,&k,&mod);
for(int i=1; i<=n; i++)
scanf("%lld",&num[i]);
init();
sort(num+1 , num+n+1 , cmp1);
for(int i=k; i<=n; i++)
ans = (ans + C(i-1,k-1)*num[i]%mod)%mod;
sort(num+1 , num+n+1 , cmp2);
for(int i=k; i<=n; i++)
ans = (ans - C(i-1,k-1)*num[i]%mod + mod)%mod;
printf("%lld",ans);
return 0;
}
swap
序列DP。
f[i][0/1]表示第i个元素是否与前一个元素发生交换。
根据题目规则我们可以知道,如果一段区间内所有的数字都发生交换,等价于这段区间的左端点到这段区间的右端点的位置,区间内其他区域统一左移。
通过这个不难写出方程,关键是转移顺序。
枚举发生交换的区间的右端点,从左往右扫一遍的话会比较麻烦,所以采取从右往左扫。
#include<iostream>
#include<cstdio>
#define INF 2147483647
using namespace std;
int n , num[2005] , f[2005][3] , sum[2005];
inline int mymin(int a , int b) { return a<b?a:b; }
inline int myabs(int a) { return a>0?a:(-a); }
int main() {
freopen("swap.in","r",stdin);
freopen("swap.out","w",stdout);
scanf("%d",&n);
for(int i=1; i<=n; i++) {
scanf("%d",&num[i]);
if(i>1)
sum[i] = sum[i-1] + myabs(num[i]-num[i-1]);
}
num[n+1] = num[n];
for(int i=n-1; i>=1; i--) {
f[i][0] = mymin(f[i+1][0]+myabs(num[i]-num[i+1]),f[i+1][1]+myabs(num[i]-num[i+2]));
f[i][1] = INF;
for(int j=i+1; j<n; j++)
f[i][1] = mymin(f[i][1],mymin(f[j+1][0]+myabs(num[i]-num[j+1]),f[j+1][1]+myabs(num[i]-num[j+2]))+sum[j]-sum[i+1]+myabs(num[i]-num[j]));
f[i][1] = mymin(f[i][1] , sum[n]-sum[i+1]+myabs(num[n]-num[i]));
}
printf("%d",mymin(f[1][0],f[1][1]));
return 0;
}