其他分享
首页 > 其他分享> > NIOP2016 DAT2

NIOP2016 DAT2

作者:互联网

NIOP2016 DAT2

组合数问题

利用杨辉三角, 前缀和优化。

$C_n ^m = C_n ^{n-m} $

$ C_n ^m = C_{n - 1} ^{m - 1} + C_{n - 1} ^m$

原谅我足够菜不知道组合数与杨辉三角有关系这一常识

#include <cstdio>
#include <iostream>
#include <cstring>
#define orz cout << "AK IOI" <<"\n"
#define int long long 

using namespace std;

inline int read()
{
	int x = 0, f = 1;
	char ch = getchar();
	while (ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while (ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48);ch = getchar();}
	return x * f;
}
int T, k, n, m, cnt[2000][2000], c[2000][2000];
bool s[2000][2000];
void init()
{
	c[0][0] = 1;
	c[1][0] = c[1][1] = 1;
	for(int i = 2; i <= 2000; i++)
	{
		c[i][0] = 1;
		for(int j = 1; j <= i; j++)
		{
			c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % k;
		    cnt[i][j] = cnt[i - 1][j] + cnt[i][j - 1] - cnt[i - 1][j - 1];
		    if(!c[i][j]) cnt[i][j]++; 
		}
		cnt[i][i + 1] = cnt[i][i];//为了更新右边 
	}
}
signed main()
{
    //freopen("problem.in","r",stdin); 
    //freopen("problem.out","w",stdout);
    T = read(), k = read(); init();
    while(T--)
    {
    	n = read(), m = read();
    	if(m > n) printf("%lld\n", cnt[n][n]);
    	else printf("%lld\n", cnt[n][m]);
	}
	return 0;
}

蚯蚓

用优先队列保存每一只蚯蚓,每一次弹出栈顶元素。

如何处理没一只蚯蚓增加一定的长度呢?转换思路。每次只有两只新产生的蚯蚓没被加,其他的全部被加了, 等价于那两只减了。 所以可以记录累计加的长度, 有几只没被加的就减去。

100分

要发现题目中的单调性。先被切掉的蚯蚓分成的蚯蚓

假设有两只蚯蚓a, b, a > b。

那么先切a,成为\(a_1, a_2\) ,t秒之后切b,此时\(a_1\) 的长度为\(a \times P + t \times q\), \(a_2\)的长度为\(a \times (1 -P) + t \times q\) , \(b_1\) 的长度为\(b \times P + t \times q\), \(b_2\)的长度为\(b \times (1 -P) + t \times q\)

可以明显的看出\(a_1 > b_1, a_2 > b_2\), 可以将这两堆分别储存,每次从三个堆之中找出最大的,切开,放回去。

#include <cstdio>
#include <iostream>
#include <queue>
#include <algorithm>
#define orz cout << "AK IOI" <<"\n"

using namespace std;
const int maxn = 7e6 + 10;

inline int read()
{
	int x = 0, f = 1;
	char ch = getchar();
	while (ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while (ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48);ch = getchar();}
	return x * f;
}
int n, m, q, u, v, t, top, add, now[maxn], cut1[maxn], cut2[maxn];
priority_queue<int> ans;
bool cmp(int a, int b)
{
	return a > b;
}
int main()
{
    //freopen("P2827_2.in","r",stdin); 
    //freopen("P2827_2.out","w",stdout);
    n = read(), m = read(), q = read(), u = read(), v = read(), t = read();
    double p = (double) u / v;
    for(int i = 1; i <= n; i++) now[i] = read();
    int t0 = n, t1 = 0, t2 = 0, h = 1, h1 = 1, h2 = 1;
	sort(now + 1, now + n + 1, cmp);
	for(int i = 1; i <= m; i++)//为了找到被切的蚯蚓    
	{
		if(h > t0)
		{
			if(cut1[h1] > cut2[h2]) top = cut1[h1++];
			else top = cut2[h2++]; 
		}
		else 
		{
			if(now[h] >= cut1[h1] && now[h] >= cut2[h2]) top = now[h++];
			else if(cut1[h1] >= cut2[h2] && now[h] <= cut1[h1]) top = cut1[h1++];
			     else top = cut2[h2++]; 
		}
		top += add;
		int left = p * (double)top, right = top - left;
		//printf("ha = %d %d\n", left, right);
		add += q;
		left -= add, right -= add;
		cut1[++t1] = left, cut2[++t2] = right;
		if(i % t == 0) printf("%d ", top);
	}
	printf("\n");
	for(int i = h; i <= t0; i++) ans.push(now[i]);
	for(int i = h1; i <= t1; i++) ans.push(cut1[i]);
	for(int i = h2; i <= t2; i++) ans.push(cut2[i]);
	
	for(int i = 1; ans.size(); i++)
	{
		if(i % t == 0) printf("%d ", ans.top() + add);
		ans.pop(); 
	} 
	return 0;
}

愤怒的小鸟

状压dp

f[S]表示已经死了的猪的集合状态为S时,最少要发射的鸟数。

f[0] = 0

f[s|(1 << (j - 1))] = min(f[s] + 1)

\(f[i|line[j][k]] = min(f[i|line[j][k]], f[i] + 1)\)

根据每两个点算出一条抛物线,然后枚举每一头猪所在的位置,能被抛物线打到的位置进行状压。

然后进行dp。

/*
状压dp !!!!!感觉有哪没理解 
*/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#define orz cout<<"AK IOI"<<"\n"

using namespace std;
const double eps = 1e-8;

inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}
int T, n, m, map[20][20], line[20][20], f[1 << 20],  s[1 << 20];
double x[20], y[20];
void init()
{
	for(int i = 0; i < (1 << 18); i++)//枚举每一种状态 
	{
		int j = 1;
		for(; j <= 18 && i & (1 << (j - 1)); j++);//i这个状态第j头猪是否被打掉 
		s[i] = j;//dp的起始位置 状态内第一个0的位置 
	}
}
void fc(double &a, double &b, int i, int j)
{
	a = -(y[i] * x[j] - y[j] * x[i]) / (x[j] * x[j] * x[i] - x[i] * x[i] * x[j]);
	b = (y[i] * x[j] * x[j] - y[j] * x[i] * x[i])/(x[i] * x[j] * x[j] - x[j] * x[i] * x[i]);
}
int main()
{
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    init();
    T = read();
    while(T--)
    {
    	memset(line, 0, sizeof line);
    	memset(f, 1, sizeof f);
    	f[0] = 0;
    	n = read(), m = read();
    	for(int i = 1; i <= n; i++) scanf("%lf%lf", &x[i], &y[i]);
    	
    	for(int i = 1; i <= n; i++)
    	{
    		for(int j = 1; j <= n; j++)
    		{
    			double a, b;
    			fc(a, b, i, j);
    			if(a > -eps) continue;//如果开口方向向上 
    			for(int k = 1; k <= n; k++)
    			{
    				if(fabs(a * x[k] * x[k] + b * x[k] - y[k]) < eps)//枚举每一个点看看是否会被打掉 
    				line[i][j]|= (1 << (k - 1));
				}
			}
		}
      	for(int i = 0; i < (1 << n); i++)
      	{ 
      		int j = s[i]; 
      		f[i|(1 << (j - 1))] = min(f[i|(1 << (j - 1))], f[i] + 1);
			for(int k = 1; k <= n; k++)
			    f[i|line[j][k]] = min(f[i|line[j][k]], f[i] + 1); 
		}
		printf("%d\n", f[(1 << n) - 1]);
	}
    //fclose(stdin);
    //fclose(stdout);
    return 0;
}

标签:NIOP2016,ch,read,times,&&,DAT2,include,蚯蚓
来源: https://www.cnblogs.com/yangchengcheng/p/14890827.html