【NOIP2018模拟赛2018.10.30】

在这里插入图片描述

区间DP

 明显的序列合并操作,dalao们想到了区间DP
 预处理0~7进行题目所示操作(实际上你会发现就是(a + b)/2)结果(O(1)算也可以),f[l][r][k]代表区间为[l,r]时,可以合并出k,转移明显是:
 if(f[l1][r1][ki] && f[l2][r2][kj]) f[l1][r2][ca[ki][kj] = 1;(ca为预处理)
 答案从0枚举到7依次输出就好。
代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pt putchar
#define gc getchar
#define ko pt(' ')
#define ex pt('\n')
const int MAXN = 505;
int a[MAXN],n,ans[MAXN];
int ca[10][10],f[MAXN][MAXN][10];
void in(int &x)
{
	int num = 0,f = 1; char ch = gc();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
	while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
	x = num*f;
}
void out(int x)
{
	if(x < 0) x = -x,pt('-');
	if(x > 9) out(x/10);
	pt(x % 10 + '0');
}

int calc(int x,int y)
{
	return (((x&y) + (x|y)) >> 1);
}

void merge(int l1,int r1,int l2,int r2)
{
	for(int i = 0;i <= 7;i++)
		if(f[l1][r1][i])
			for(int j = 0;j <= 7;j++)
				if(f[l2][r2][j])
					f[l1][r2][ca[i][j]] = 1;
}

int main()
{
	in(n);
	for(int i = 1;i <= n;i++) in(a[i]),f[i][i][a[i]] = 1;
	for(int i = 0;i <= 7;i++)
		for(int j = 0;j <= 7;j++)
			ca[i][j] = calc(i,j);
	for(int len = 0;len < n;len++)
		for(int l = 1;l + len <= n;l++)
			for(int k = l;k < l + len;k++)
				merge(l,k,k+1,len+l);
	for(int i = 0;i <= 7;i++)
		if(f[1][n][i])
			ans[++ans[0]] = i;
	for(int i = 1;i < ans[0];i++) out(ans[i]),ko;
	out(ans[ans[0]]);
	return 0;
}
/*
4
1 4 3 2
*/

在这里插入图片描述

二分图处理行列问题

 看到行列问题自然想到可以用二分图处理,答案就是最大独立集点数。本质上来说,就是最多能选出多少个点,使得有连边的点不在同一个集合。(对于本题来说,就是保证不重复选择一个不好惹的同学)
由于 最大独立集点数 = 顶点数 - 最大二分图匹配数
匈牙利算法即可。
代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pt putchar
#define gc getchar
#define ko pt(' ')
#define ex pt('\n')
const int MAXN = 2e3 + 5;
const int INF = 999999999;
int n,m;
ll ans = 0,sum = 0;
struct edge
{
	int next,to;
}e[MAXN<<1];
int head[MAXN<<1],cnt = 0,match[MAXN],rt[MAXN];
void add(int u,int v)
{
	e[++cnt].next = head[u]; e[cnt].to = v; head[u] = cnt;
}
void in(int &x)
{
	int num = 0,f = 1; char ch = gc();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = gc();}
	while(ch >= '0' && ch <= '9') {num = (num<<3) + (num<<1) + (ch-'0'); ch = gc();}
	x = num*f;
}
void out(ll x)
{
	if(x < 0) x = -x,pt('-');
	if(x > 9) out(x/10);
	pt(x % 10 + '0');
}

bool link(int x,int fr)
{
	rt[x] = fr;
	for(int i = head[x];i;i = e[i].next)
	{
		int to = e[i].to;
		if(!match[to] || (rt[match[to]] != fr && link(match[to],fr))){
			match[to] = x; return 1;
		}
	}
	return 0;
}


int main()
{
	in(n); in(m);
	for(int i = 1;i <= m;i++) 
	{
		int x,y; in(x),in(y);
		add(x,y+n);
	}
	for(int i = 1;i <= n;i++) if(link(i,i)) sum++;
	ans = ((n<<1) - sum) * n;
	out(ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/chang_yl/article/details/83549458