hdu-4873-ZCC Loves Intersection
problem
1. 输入n[1, 1e9]和d[2, 99].
2. 表示在d维空间,每一维是[0, n-1].
3. 现在随机等概率取d个线段,分别平行于d个轴。
4. 这样就取了d个线段,每两个线段相交就有一个交点,
5. (m个线段交于一点,就是有m-1个点,从官方题解种理解到的这句话。)
6. 求交点个数期望。
think
1. 我们先考虑两个线段的情况。
他俩共面的概率是1/n^(d-2), 因为除了他俩在的两维,其他维都一样就是共面了。
现在我们这考虑这两个线段在这两维的情况就可以了。
设第一个线段是[x0, y1], [x0, y2]
设第二个线段是[x1, y0], [x2, y0]
那么他俩相交的概率就是(p(x1<=x0<=x2))^2 (因为y是相同的求法相同的概率,所以我们平方就可以了。)
p(x1<=x0<=x2) = (2*(n-1) + 3*(n-2) + …… + n*(n-(n-1)))/ (C(n, 2) * n)
= ( 2n+3n+……nn - (1*2+2*3+……+(n-1)n) ) * 2 / nn(n-1)
= (n+4) / 3n
2. 这是两个线段的。一共有C(d, 2)种两个线段。所以最后答案就是:
C(d, 2) * (1/n^(d-2)) * (p(x1<=x0<=x2))^2 = C(d, 2) * (n+4)^2 / (3^2 * n^d)
3. 需要高精度。
分子分为两个数,C(d, 2) * (n+4) 和 (n+4), 分母分为三部分,3的指数,n的指数,n和分子约分后剩下的数(令这个数为x)。
这样当分子的第一个数约分后,就让第二个数与x约分,这里要防止爆long long.
code
LL bit[2][111111];//用于模拟分子的高精度
LL fenmu[2];//分别表示3和n的指数
LL fenzi[2];//表示这2个数相乘
LL n, d;
int cnt[2];
void merge(int id){
for(int j = 0; j < cnt[id]; ++j){
bit[id][j+1] += bit[id][j] / 10LL;
bit[id][j] %= 10LL;
}
while(bit[id][cnt[id]]){
bit[id][cnt[id]+1] = bit[id][cnt[id]]/10LL;
bit[id][cnt[id]] %= 10LL;
++cnt[id];
}
}
int main(){
// freopen("data.txt", "r", stdin);
// freopen("a1.out","w",stdout);
LL g;
while(scanf("%I64d%I64d", &n, &d) != EOF){
fenmu[0] = 2; //3的指数
fenmu[1] = d; //n的指数
memset(bit, 0, sizeof(bit));
bit[0][0] = 1;
bit[1][0] = 1;
fenzi[0] = d * (d-1) / 2 * (n+4);
fenzi[1] = n+4;
cnt[0] = 1;
cnt[1] = 1;
for(int i = 0; i <= 1; ++i){
while(fenmu[0] >= 1){
g = __gcd(3LL, fenzi[i]);
if(g == 1) break;
fenzi[i] /= g;
--fenmu[0];
}
}
while(fenmu[1] >= 1){
LL g = __gcd(n, fenzi[0]);
if(g == 1) break;
fenzi[0] /= g;
--fenmu[1];
LL tmp = n/g;
LL gg = __gcd(tmp, fenzi[1]);
fenzi[1] /= gg;
for(int j = 0; j < cnt[1]; ++j) bit[1][j] *= tmp/gg;
merge(1);
}
while(fenmu[1] >= 1){
LL g = __gcd(n, fenzi[1]);
if(g == 1) break;
fenzi[1] /= g;
--fenmu[1];
for(int j = 0; j < cnt[1]; ++j) bit[1][j] *= n/g;
merge(1);
}
bit[0][0] = fenzi[0];
merge(0);
for(int j = 0; j < cnt[0]; ++j) bit[0][j] *= fenzi[1];
merge(0);
for(int i = 0; i < fenmu[0]; ++i){
for(int j = 0; j < cnt[1]; ++j) bit[1][j] *= 3;
merge(1);
}
merge(1);
for(int i = 0; i < fenmu[1]; ++i){
for(int j = 0; j < cnt[1]; ++j) bit[1][j] *= n;
merge(1);
}
for(int i = cnt[0]-1; i >= 0; --i) cout<<bit[0][i];
if(!(cnt[1]==1 && bit[1][0]==1)){
printf("/");
for(int i = cnt[1] - 1; i >= 0; --i) cout<<bit[1][i];
}
puts("");
}
return 0;
}
hdu-4876-ZCC loves cards
problem
1. 输入n[1, 20], k[1, min(n, 6)], L[1, 100], n个ai[1, 100]
2. 给你n个数,分别是ai,从中任选k个,任意排成一个环,排完就不许变顺序了。
3. 这时,从环中任取m个连续的数,得到一个亦或和的值。由于位置和m任意,所以可以得到很多亦或和的值。
4. 在这很多的亦或和的值里面,找到包含[L, R]这个区间里面的所有整数的最大的R。如果没有输出0.
think
1. 先设R = L, 然后慢慢更新变大R.
2. 首先取k个数,这样复杂度是C(n, k).
3. O((1<<k)*k + (R-L+1)) 判断不考虑顺序是否包含[L, R]; 如果不包含,这k个数是不行的;
4. 如果包括,k!枚举顺序。然后k^2判断时候可以。
5. 总复杂度是O( C(n, k) * ((1<<k)*k + (R-L+1) + p * k! * k^2) ) 其中p是可以执行第4步的概率。
code
int a[22];
int b[10];
int vis[130];
int op[10];
bool flag;
int n, k, L, R;
bool ok(){
memset(vis, 0, sizeof(vis));
for(int i = 0; i < (1<<k); ++i){
int ans = 0;
for(int j = 0; j < k; ++j){
if(i & (1<<j)) ans = (ans ^ b[j]);
}
vis[ans] = 1;
}
for(int i = L; i <= R; ++i) if(!vis[i]) return false;
return true;
}
void dfs(int i1, int i2){
if(i2 == k){
if(ok()){
flag = true;
int sum = 0;
for(int i = 0; i < k; ++i) sum = (sum ^ b[i]);
do{
memset(vis, 0, sizeof(vis));
op[0] = b[0];
vis[b[0]] = 1;
vis[sum^b[0]] = 1;
for(int i = 1; i < k; ++i){
op[i] = (op[i-1] ^ b[i]);
vis[op[i]] = 1;
vis[sum^op[i]] = 1;
for(int j = 0; j < i; ++j){
int tmp = op[i] ^ op[j];
vis[tmp] = 1;
vis[sum ^ tmp] = 1;
}
}
for(int i = L; ; ++i){
if(!vis[i]){
R = max(R, i - 1);
break;
}
}
}while(next_permutation(b, b + k));
}
}
for(int i = i1; i < n; ++i){
b[i2] = a[i];
dfs(i+1, i2+1);
}
}
int main(){
int T, tt = 0;
while(scanf("%d%d%d", &n, &k, &L) != EOF){
R = L;
for(int i = 0; i < n; ++i) scanf("%d", &a[i]);
sort(a, a + n);
flag = 0;
dfs(0, 0);
R = flag * R;
printf("%d\n", R);
}
return 0;
}
hdu-4882-ZCC Loves Codefires
problem
n个东西。每个东西有两个值。Ei和Ti。每个东西的花费是 (他前面的Ti和+Ti)*Ei
求最小的总花费和。
think
看数据范围像贪心。
对于两个东西,设他们是Ti,Ei,Tj,Ej,前面T和是T,
i放前面是 (T + Ti)*Ei + (T + Ti + Tj)*Ej
j放前面是 (T + Ti + Tj)*Ei + (T + Tj)*Ej
就看Ti*Ej 和Tj*Ei 谁更小。
但是并不会严格证明这是对的。官方结题报告也没有给出严格证明。只是想想觉得这就是对的。
code
const int N = 111111;
struct point{
LL E, K;
}p[N];
bool cmp(point x, point y){
LL a = x.E*x.K + (x.E+y.E)*y.K;
LL b = y.E*y.K + (y.E+x.E)*x.K;
return a < b;
}
int main () {
int n;
while(scanf("%d", &n) != EOF){
for(int i = 0; i < n; ++i) scanf("%I64d", &p[i].E);
for(int i = 0; i < n; ++i) {
scanf("%I64d", &p[i].K);
}
sort(p, p + n, cmp);
LL ans = 0;
LL T = 0;
for(int i = 0; i < n; ++i){
T += p[i].E;
ans += T * p[i].K;
}
printf("%I64d\n", ans);
}
return 0;
}