区间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;
}