2
26
2015
0

考试题解与反思

T1:统计满足Ai mod Ci != 0且Ai & Ai+1 = 0的序列Ai数量。
    Hint:20%的数据 N=10,M=2;另有40%的数据N=50,M=5;剩余的数据N=50,M=15。
Solutions:
    20分算法:暴力搜索即可;
    60分算法:据说还有暴力方法可以拿到60分。
    100分算法:学长推倒出了神奇的矩阵:可以用于表示出对于序列中的第k个元素取i值d[i] = j,j为可行方案数量。

如图所示是一个4*4的该矩阵,横行i表示第k个元素可以取的i值d[i],纵列表示取d[i]时产生的解的数量,现在要做的实际上是通过这样一个4*4的子矩阵利用Dp求出一个2^m*2^m的矩阵。

 

   

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define N 50 + 5
#define M 1 << 15
#define Mod 1000000000

int n, m, ans, C[N], Dp[M], Dp2[M];

inline int add(int a, int b)
{
    return a + b - (a + b >= Mod ? Mod : 0);
}
inline void solve(int l, int r)
{
    if (l == r - 1)
    {
        Dp2[l] = Dp[l];
        return ;
    }
    int mid = l + r >> 1;
    solve(l, mid);
    solve(mid, r);
    for (int i = l; i < mid; i ++)
    {
        int t = Dp2[i];
        Dp2[i] = add(Dp2[i], Dp2[i + mid - l]);
        Dp2[i + mid - l] = t;
    }
}
int main(){
    freopen("tle.in", "r", stdin);
    freopen("tle.out", "w", stdout);
    
	scanf("%d%d", &n, &m);
    for (int i = n - 1; i >= 0; i --)
        scanf("%d",  &C[i]);
    Dp[0] = 1;
	while (n --)
    {
        solve(0, 1 << m);
        for (int i = 0; i < (1 << m); i ++)
            Dp[i] = i % C[n] ? Dp2[i] : 0, Dp2[i] = 0;
    }
	for(int i = 0;i < (1 << m); i ++)
		ans = add(ans,Dp[i]);
	printf("%d\n",ans);
	
	fclose(stdin);
	fclose(stdout);
	return 0;
}

 

T2:题目背景略(Lovelive= =)
       给出n个操作,每个操作含t,x,y。t =1维护:增加一个元素,smile值为x,pure值为y;t = 0维护:询问对于一首歌曲

(p,q);最大的和适度:ans = p*x + q*y;t=-1维护:删除smile值为x,pure值为y的元素。
     Hint:有40%的数据n=1000;有40%的数据n=15000;剩下的数据n=50000。
Solutions:
    100分算法I:裸暴力,用数组维护内存会大一些,若用动态链表维护内存就更大了,貌似数组比链表快?
    100分算法II:转化为计算几何问题,用分块 + 凸包处理(表示不会)。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
#define maxn 50000 + 5
#define INF 1000000000
int n,vis = 0,t;
long long p,q;
struct Node{
	ll x,y;
	bool ok;
}a[maxn];

int main()
{
	//freopen("ll.in","r",stdin);
	//freopen("ll.out","w",stdout);
	memset(a,0,sizeof(a));
	scanf("%d",&n);
	for(int i = 1;i <= n; i++){
		scanf("%d%lld%lld",&t,&p,&q);
		if(t == 1){
			a[vis].x = p;
			a[vis].y = q;
			a[vis].ok = 1;
			vis ++;
		}
		if(t == 0){
			ll Max = -INF;
			for(int i = 0;i < vis; i++)
				if(a[i].ok && (p*a[i].x + q*a[i].y)>Max)Max = p*a[i].x + q*a[i].y;
			printf("%lld\n",Max);
		}
		if(t == -1){
			for(int i = 0;i < vis; i++)
				if(a[i].x==p && a[i].y==q){
					a[i].ok = 0;break;
				}
		}
	}
	//fclose(stdin);
	//fclose(stdout);
	return 0;
}

 

T3:两个人(Alice、Bob)取巧克力,每个巧克力有一个能量等级r和可口度s。两个人也各有一个能量等级A、B,每个人有

Eat和Pass两种操作,Pass操作会消耗能量等级。若两人足够聪明,求巧克力取完是两人的满足度(吃到巧克力可口度的和)。
    Hint:其中有30%的数据1≤N≤10, 0≤A,B,r≤1,000,000,000
           另外有30%的数据1≤N≤50, 0≤A,B,r≤10
           剩下的数据1≤N≤150, 0≤A,B,r≤1,000,000,000
           对于所有的数据满足0≤s, 且s的和小于150
Sulotions:
    30分算法:直接搜索所有可能的状态,对于大数据TLE是必然(100个测试点的题目也是罕见= =)。
    100分算法:DP,对Alice和Bob分别建立两个状态图,从Alice和Bob的角度分别DP一次,再维护最优解(对于多维数组的函数,可以考虑使用模板(Template<Class T> T 函数名(T 变量......))。(表示不会)看了一下std,感觉std那个方法就是模拟两人的操作过程再直接输出(其实这其中也有Dp的成分)?用F[i][j]表示状态:i表示第i块巧克力,j表示当前该人的能量等级,F[i][j]表示初始能量等级的最小值。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define maxn 150 + 5
typedef long long ll; 
#define INF 1LL<<60
int N,sum = 0,s[maxn];
ll A,B,r[maxn],tmp;
ll FA[maxn][maxn],FB[maxn][maxn];

template<class T> inline T &gmin(T &a,const T &b){
	if(a > b)a = b;return a;
}
template<class T> inline T &gmax(T &a,const T &b){
	if(a < b)a = b;return a;
}
/*Process of Alice's process
  For F[i][j]:i means the i-th chocolate;
  j means the person's present scores;
  and F[i][j] means the person's minnum enegy levels.
*/
inline void Alice(int i){
	for(int j = sum; j >= 0; j--){
		tmp = INF;
		gmin(tmp,FA[i][j + 1]);
		if(j >= s[i]) gmin(tmp, FB[i + 1][j - s[i]] - r[i]);
		gmin(tmp, max(1LL,FA[i + 1][j] + r[i] + 1));
		FA[i][j] = tmp;
	}
	return;
}
/*Process of Bob's process*/
inline void Bob(int i){
	for(int j = 0;j <= sum; j++){
		tmp = -INF;
		if(j)gmax(tmp,FB[i][j - 1]);
		gmax(tmp,FA[i+1][j] + r[i]);
		if(j >= s[i]) gmax(tmp, min(1LL,FB[i+1][j - s[i]] - r[i] - 1));
		FB[i][j] = tmp;
	}
	return;
}

int main(){
	freopen("sw.in","r",stdin);
	freopen("sw.out","w",stdout);
	
	scanf("%d%lld%lld",&N,&A,&B);
	for(int i = 0;i < N; i++){
		scanf("%lld%d",&r[i],&s[i]);
		sum += s[i];
	}
	for(int i = 0;i < maxn; i++)
		for(int j = 0;j < maxn; j++){
			FA[i][j] = INF;
			FB[i][j] = INF;
		}
	FA[N][0] = -INF;
	FB[N][0] = -INF;

	/*Process of the operations */
	for(int i = N; i--;){
		Alice(i);
		Bob(i);
	}
	
	int ans = 0;
	for(int i = 0;i <= sum; i++)
		if(FA[0][i] <= A-B) ans = i;
	printf("%d %d\n", ans, sum - ans);
	
	fclose(stdin);
	fclose(stdout);
	return 0;
}

 

 

本次题目无论难度如何,爆零这种事发生既是令人不爽也是不能容忍的,尤其是第二题,这一次是因为把一个该做局部变量的

变量做全局变量。多次在这样的签到题上卡住也说明代码实现的一些细节没有搞清楚,然而正所谓细节决定成败,因此以后在

做题目的过程中要多注意这些细节。

Category: | Tags: | Read Count: 626

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter

Host by is-Programmer.com | Power by Chito 1.3.3 beta | Theme: Aeros 2.0 by TheBuckmaker.com