悲剧的一场比赛,感觉败在英语上了,题意理解错误题目肯定做不出来。
第一题看懂就可以直接写:
A:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<algorithm>
#include<stack>
#include<deque>
#include<set>
#include<vector>
#include<iomanip>
#include<cctype>
#include<string>
#include<memory>
#include<map>
#include<sstream>
#define mem(a) memset(a, 0, sizeof(a))
#define Forr(n) for (int ii = 0; ii < n; ++ii)
#define sl(a) strlen(a)
typedef long long LL;
typedef double dou;
const int Mod = 1000000007;
const double eps = 1e-8;
const LL inf = (LL) (1e18 + 0.5);
const int inf1 =(LL) (1e9 + 0.5);
const int N = 100005;
using namespace std;
int main()
{
LL n, i, j, k, re = 0;
char s[2005] = {0};
cin >> n;
cin >> s;
for (i = n; i < sl(s); i += n)
{
if (s[i - 1] == s[i - 2] && s[i - 2] == s[i - 3])
re++;
}
cout << re << endl;
return 0;
}
B显然是一个动态规划,怎么DP呢?一开始的想法是O(n)算出长度为k的所有区间和s[n - k],每个s[i]和自己区间最左边的下标组成一个结构体,然后降序排序这样和最大的在左边,下标 0 记为st,只需要从左往右找到和它下标差大于等于k的第一个s[i]即可这个下表 i 记为en,然后只需在st,en中再查找和比s[st] + s[en] 大的两个数,更新st,en,最后结果是p[st].n,p[en].n。代码如下:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<algorithm>
#include<stack>
#include<deque>
#include<set>
#include<vector>
#include<iomanip>
#include<cctype>
#include<string>
#include<memory>
#include<map>
#include<sstream>
#define mem(a) memset(a, 0, sizeof(a))
#define Forr(n) for (int ii = 0; ii < n; ++ii)
#define sl(a) strlen(a)
typedef long long LL;
typedef double dou;
const int Mod = 1000000007;
const double eps = 1e-8;
const LL inf = 1e18;
const int inf1 = 1e9;
const int N = 200005;
using namespace std;
LL a[N], s[N];
struct pp
{
LL sum, n;
}p[200005];
bool cmp(pp a, pp b)
{
if (a.sum == b.sum)
return a.n < b.n;
return a.sum > b.sum;
}
LL search(LL n, LL en, LL k)
{
LL i;
for (i = n + 1; i < en; ++i)
if (abs(p[n].n - p[i].n) >= k)
return i;
return -1;
}
int main()
{
LL n, i, j, k, st, en, max, tem;
cin >> n >> k;
for (i = 0; i < n; ++i)
cin >> a[i];
for (i = 0; i < k; ++i)
s[0] += a[i];
p[0].sum = s[0];
p[0].n = 0;
for (i = 1; i <= n - k; ++i)
{
s[i] = s[i - 1] - a[i - 1] + a[i + k - 1];
p[i].sum = s[i];
p[i].n = i;
}
sort(p, p + n, cmp);
st = max = 0;
en = n - 1;
for (i = 0; i < n; ++i)
if (abs(p[0].n - p[i].n) >= k)
{
en = i;
max = p[0].sum + p[i].sum;
break;
}
for (i = 1; i < en; ++i)
{
tem = search(i, en, k);
if (tem > 0)
{
if (p[i].sum + p[tem].sum > p[st].sum + p[en].sum)
{
st = i;
en = tem;
}
}
}
if (p[st].n > p[en].n)
{
tem = st;
st = en;
en = tem;
}
cout << p[st].n + 1 << ' ' << p[en].n + 1 << endl;
return 0;
}
很不幸,T,原因很明显,这个算法在坏数据下是退化为O(n^2)的,题目的数据规模200000,显然T。
想想别的方法吧,需要一个O(n)的算法,这个问题是个求最大值的问题,如果对于每个下标为 i 开始长度为k的区间,如果知道了在下标为i +k之后的所有长度为k的区间的和的最大值,那么,让i从0,跑到n-k,不断更新st,en,即可,可是如何在O(n)时间内算出下标i+k之后的区间里s的最大值呢?如果从左往右算的话显然是O(n^2)显然不行,但是这其实是个很经典的问题啊,(这次我一定记住了!)我们可以从右往左,不断更新最大值,如果新加的数比原来下标时的最大值小,则最大值不变,否则更新最大值,赋值给它,这样就做到O(n)啦!!!
代码如下:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<algorithm>
#include<stack>
#include<deque>
#include<set>
#include<vector>
#include<iomanip>
#include<cctype>
#include<string>
#include<memory>
#include<map>
#include<sstream>
#define mem(a) memset(a, 0, sizeof(a))
#define Forr(n) for (int ii = 0; ii < n; ++ii)
#define sl(a) strlen(a)
#pragma warning (disable : 4996)
typedef long long LL;
typedef double dou;
const int Mod = 1000000007;
const double eps = 1e-8;
const LL inf = 1e18;
const int inf1 = 1e9;
const int N = 200005;
using namespace std;
LL a[N], s[N], ma[N], ind[N];
int main()
{
LL n, i, j, k, st, en, tem;
cin >> n >> k;
for (i = 0; i < n; ++i)
scanf("%I64d", a + i);
for (i = 0; i < k; ++i)
s[0] += a[i];
for (i = 1; i <= n - k; ++i)
s[i] = s[i - 1] - a[i - 1] + a[i + k - 1];
ma[n - k] = s[n - k];
ind[n - k] = n - k;
for (i = n - k - 1; i >= 0; i--)
{
if (s[i] >= ma[i + 1])
{
ma[i] = s[i];
ind[i] = i;
}
else
{
ma[i] = ma[i + 1];
ind[i] = ind[i + 1];
}
}
st = 0, en = ind[k];
tem = s[0] + s[ind[k]];
for (i = 1; i <= n - 2 * k; ++i)
{
if (s[i] + s[ind[i + k]] > tem)
{
st = i;
en = ind[i + k];
tem = s[i] + s[ind[i + k]];
}
}
cout << st + 1 << ' ' << en + 1 << endl;
return 0;
}
AC,很开心!!!这种思想可以解决好多这样的求最值问题,可以使最小值等。
C:
贪心,先按b升序同时a升序排序,那么前p-k个一定不可能被选,然后从其他方案中选择这是保证a最大,最后这个最大值显然是最优的,然后调整b最大,这是要和最大值集合里b最小的方案比较,b比最小值还小的方案可能被选。代码如下:
#include <cstdio>
#include <cstdlib>
struct xxx
{
int a,b,num,num2;
}x[111111];
int ans[111111],pp=0;
int comp1(const void *m,const void *n)
{
return (*(struct xxx *)n).b-(*(struct xxx *)m).b;
}
int comp2(const void *m,const void *n)
{
return (*(struct xxx *)n).a-(*(struct xxx *)m).a;
}
int comp3(const void *m,const void *n)
{
return (*(struct xxx *)m).a-(*(struct xxx *)n).a;
}
int comp4(const void *m,const void *n)
{
return (*(struct xxx *)m).num2-(*(struct xxx *)n).num2;
}
int main()
{
int n,p,k;
pp=0;
scanf("%d%d%d",&n,&p,&k);
for (int i=0;i<n;i++)
scanf("%d%d",&x[i].a,&x[i].b),x[i].num=i+1;
qsort(x,n,sizeof(x[0]),comp1);
for (int i=0;i<n;i++)
{
int j;
for (j=i+1;x[j].b==x[i].b;j++) ;
qsort(x+i,j-i,sizeof(x[0]),comp3);
i=j-1;
}
for (int i=0;i<n;i++)
x[i].num2=i+1;
qsort(x,n-(p-k),sizeof(x[0]),comp2);
int i,j,max=0;
for (i=k;i<n&&x[i].a==x[i-1].a;i++) ;
for (j=k-2;j>=0&&x[j].a==x[j+1].a;j--) ;
qsort(x+j+1,i-j-1,sizeof(x[0]),comp4);
for (int i=0;i<k;i++)
{
ans[pp++]=x[i].num;
if (x[i].num2>max) max=x[i].num2;
}
for (int i=k;i<n;i++)
if (x[i].num2>max&&x[i].num2<=max+p-k)
ans[pp++]=x[i].num;
for (int i=0;i<pp;i++)
printf("%d ",ans[i]);
printf("\n");
return 0;
}
D是一个图论边度数的计数,算两次思想,(考察每个点引出的边,再考察总体边的权值)很容易写出表达式,(理解错题意的话结果就不一样了。。。。T^T)读入数据用scanf,cin Tle了两次。注意考虑精度误差,技巧就是先乘以一个大数再最后除去,代码如下:
D:
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<algorithm>
#include<stack>
#include<deque>
#include<set>
#include<vector>
#include<iomanip>
#include<cctype>
#include<string>
#include<memory>
#include<map>
#include<sstream>
#define mem(a) memset(a, 0, sizeof(a))
#define Forr(n) for (i = 0; i < n; ++i)
#define sl(a) strlen(a)
typedef long long LL;
typedef double dou;
const int Mod = 1000000007;
const double eps = 1e-8;
const LL inf = 1e18;
const int inf1 = 1e9;
const int N = 100005;
using namespace std;
dou x[2005];
int s[2005];
struct po
{
LL n, sum;
}p[200005];
dou mul(LL n, LL N, LL k)
{
LL i;
if (s[n] == 0)
{
dou a = (dou) n, b = (dou) N, c = (dou) k, d = 1.0;
for (i = 0; i < k; ++i)
d *= ((a - (dou) i) / (b - (dou) i));
x[n] = d * c / a;
s[n] = 1;
return x[n];
}
else
return x[n];
}
int main()
{
LL n, i, j, k, x;
double y = 0;
LL sum = 0;
mem(p);
cin >> n >> k;
for (i = 0; i < n; ++i)
for (j = i + 1; j < n; ++j)
{
scanf("%I64d", &x);
if (x != -1)
{
p[i].n++;
p[j].n++;
p[i].sum += x;
p[j].sum += x;
}
}
for (i = 0; i < n; ++i)
{
if (p[i].n >= k)
y += (mul(p[i].n, n, k) * (dou) p[i].sum * 10000.0);
}
cout << (LL) ((y + 0.5) / 10000.0) << endl;
return 0;
}
D的CF解题指导上给了一个图论定理,k>= 3时满足题意条件的只有完全图,直接算,所以只需要计算k=1,2。这样也是可以的,以上解法没有用这个知识,我觉得更好一点吧。