NOIP2016 DAY2


組合數問題

利用楊輝三角, 前綴和優化。

$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\) ,t秒之后b的長度變為了\((b + t * q) * p\)

\(b_1\) 的長度為$b \times P + t \times q \times p $, \(b_2\)的長度為\(b \times (1 -P) + t \times q \times (1 - p)\)

可以明顯的看出\(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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM