您的位置:首页 > 其它

sduacm16级寒假训练 自测

2017-02-16 16:15 351 查看

POJ 3579 Median

【sol】

显然,每个点与其他的点形成的差是有序的,这样相当于有n组升序的序列,询问中位数。二分中位数,然

后判断比他小的数的个数,复杂度n* logn*logn

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N = 100000 + 50;
int a
, n, m;
bool cal(int x)
{
int num = 0;
for(int i = 0; i < n; i++)
num += n - (lower_bound(a, a + n, a[i] + x) - a);
return(num > m / 2);
}
int main()
{
while(~scanf("%d", &n))
{
m = n * (n - 1) / 2;
for(int i = 0; i < n; i++)
scanf("%d", &a[i]);
sort(a, a + n);
int l = 0,  r = 1e9 + 5;
int ans;
while(l <= r)
{
int mid = (l + r) >> 1;
if (cal(mid))
{
ans = mid;
// cout<<"fu"<<ans<<endl;
l = mid + 1;
} else
r = mid - 1;
}
printf("%d\n", ans);
}
return 0;
}


POJ 1742 Coins

【sol】

裸的多重背包,但是普通的多重背包会超时,但是如果一件物品的总体积如果超过背包总体积的话,完全可

以用完全背包搞,这样可以优化时间复杂度。

#include<cstdio>
#include<iostream>
#include<cstring>
const int N = 100000 + 50;
bool f
;
int a
, c
, v
, top, ans;
using namespace std;
int n, m;
void cal(int x, int num)
{
int tot = 1;
while(num >= tot)
{
v[++top] = tot * x;
num -= tot;
tot *= 2;
}
if (num)
v[++top] = num * x;
}
int main()
{
while(~scanf("%d%d", &n, &m) && n)
{
memset(f, 0, sizeof(f));
top = 0;
ans = 0;
for(int i = 0; i < n; i++)
scanf("%d", &a[i]);
for(int i = 0; i< n; i++)
scanf("%d", &c[i]);
for(int i = 0; i < n; i++)
if(a[i] * c[i] < m)
cal(a[i], c[i]);
f[0] = true;
for(int i = 1; i <= top; i++)
for(int j = m; j >= v[i]; j--)
f[j] = f[j] || f[j - v[i]];
for(int i = 0; i < n; i++)
if(a[i] * c[i] >= m)
for(int j = a[i]; j <= m; j++)
f[j] = f[j] || f[j - a[i]];
for(int j = 1; j <= m; j++)
if (f[j])
ans++;
printf("%d\n", ans);
}
return 0;
}


Visible Lattice Points

没找到来源………..

【Pro】

给定整数n, 询问有多少对(i,j),(i,j < n),使得gcd(i,j) = 1,有多组数据

【sol】

完全可以先暴力出来所有答案(没错,就是打表,,,,),如果这道题的数据范围变大的话,其实可以用

欧拉函数来搞,即ans = ∑f(i),先用nlogn来搞出f(i)的值…

#include<cstdio>
#include<iostream>
#include<cstring>
const int N = 1000 + 50;
int f
, Case = 0;
using namespace std;
int n, m, T;
int gcd(int a, int b)
{
if(b == 0)
return a;
return gcd(b, a % b);
}
int main()
{
f[1] = 3;
for(int t = 2; t <= 1000; t++)
{
f[t] = f[t - 1];
for(int i = 1; i < t; i++)
if (gcd(i, t) == 1)
f[t] += 2;
}
scanf("%d", &T);
while(T--)
{
scanf("%d", &n);
printf("%d %d %d\n", ++Case, n, f
);
}
return 0;
}


POJ 1845 Sumdiv(数论,求A^B的所有约数和)

【sol】

先质因数分解一下,假设为2^a1 * 3^ a2* 5^a3然后因子和就是(2^0 + 2^ 1 + 2^ 2+…..+2^ a1)*(3^0 +

3^1 + …..+3^ a2) *(5^0 + …. + 5^a3),这样每项用等比数列公式来搞就可以了,但是公式有除法,就需要

逆元了

【吐槽】

wa掉了好多发,注意两个问题,第一,即公比为1的时候特殊处理,第二,当p为9901时,此时结果并不为

零…毕竟0^0 = 1

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long LL;
const int N = 10000 + 50;
const int P = 9901;
LL p
, a
, top = 0, n, m, inv
;

void cal(LL n)
{
for(int i = 2; i <= sqrt(n); i++)
if (n % i == 0)
{
top++;
p[top] = i;
while (n % i == 0)
{
a[top]++;
n /= i;
}
}
if (n > 1)
{
p[++top] = n;
a[top] = 1;
}
}
LL quick(LL a, LL b)
{
LL ans = 1;
a = a % P;
while(b)
{
if (b % 2 == 1)
ans = ans * a % P;
b /= 2;
a = a * a % P;
}
return ans;
}
int main()
{
inv[1] = 1;
for(int i = 2; i < P; i++)
{
inv[i] = (P - P / i) * inv[P % i];
inv[i] %= P;
}
while(cin>>n>>m){
if (n == 0)
{
printf("0");
continue;
}
top = 0;
memset(a, 0, sizeof(a));
cal(n);
LL ans = 1;
for(int i = 1; i <= top; i++)
{
p[i] %= P;
if (p[i] == 0)
{
// printf("0");
// ans = 1;
continue;
}
if(p[i] == 1)
{
LL tot = a[i] * m + 1;
tot %= P;
ans = ans * tot % P;
continue;
}
// cout<<"test"<<p[i]<<"ko"<<a[i] * m + 1<<endl;
LL tot = quick(p[i], a[i] * m + 1);
//  cout<<"tot"<<tot<<endl;
tot = (tot - 1 + P);
tot = (tot % P )* inv[p[i] - 1];
// cout<<"tot"<<tot<<endl;
ans = ans * tot % P;
}
cout<<ans;
}
return 0;
}


POJ1837 Balance

【sol】

此问题等价于一组物品中只能选取一件物品,询问某价值的方案数,即把一系列的挂钩的权重与砝码的乘积

组成一组物品,这里的权重会有负数,最后的答案就是f[0]

#include<cstdio>
#include<iostream>
using namespace std;
const int N = 5000 * 2;
int f[20 + 5]
;
int w[20 + 5], a[20 + 5];
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for(int i = 0; i < n; i++)
scanf("%d", &a[i]);
for(int i = 0; i < m; i++)
scanf("%d", &w[i]);
f[0][5000] = 1;
for(int i = 0; i < m; i++)
{
for(int j = 0; j <= 10000; j++)
if (f[i][j] != 0)
for(int k = 0; k < n; k++)
{
int t = j + a[k] * w[i];
f[i + 1][t] += f[i][j];
//  printf("f[%d,%d]=%d\n", i + 1, t, f[i + 1][t]);
}
}
printf("%d", f[m][5000]);
return 0;
}


POJ 3267 The Cow Lexicon

【sol】

暴力,显然不现实,用dp来搞,f[i]代表前i个字符的最大匹配长度,这样f[i] = f[j] + new,这样考虑每一步

的决策,即判断一个字符串是否是另外一个字符串的子串,可以用O(n)来搞,但是对于每个区间,都需要与

每个单词匹配一下,这样如果m个单词,长度为n的字符串,复杂度为n* n* m*m,我们需要优化,如果字

符串的(l,r)可以匹配某单词的话,(l,r + 1),(l,r + 2)都可以了,所以预处理的时候枚举起点,做一次匹配,这

样对于不同的终点不需要再次匹配了

#include<cstdio>
#include<iostream>
using namespace std;
string a[600 + 5];
string ss;
const int N = 300 + 5;
int n, m;
int tot

, f
;
int cal(int t, string s2)
{
int l = 0;
int k = ss.length();
for(int i = t; i < ss.length(); i++)
{
if (ss[i] == s2[l])
l++;
if (l == s2.length())
{
k = i;
break;
}
}
return k;
}
int main()
{
scanf("%d%d", &m, &n);
cin>>ss;
for(int i = 0; i < m; i++)
cin>>a[i];
for(int i = 0; i < n; i++)
{
for(int j = 0; j < m; j++)
{
int t = cal(i, a[j]);
//  if (t != ss.length())
//  printf("%d ~ %d\n", i, t);
int ans = a[j].length();
for(int k = t; k < ss.length(); k++)
tot[i][k] = max(tot[i][k], ans);
}
}
for(int i = 1; i <= n; i++)
for(int j = 0; j < i; j++)
f[i] = max(f[i], f[j] + tot[j][i - 1]);
printf("%d", n - f
);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  poj