1. 程式人生 > >【NOIP2018模擬賽2018.10.30】

【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;
}