J:画直线
思路:看到数据范围差不多可以想到用来装压dp,一个比较重要的观察是,每次都从已经染色的点上出发画新线,就是最优做法了,每次都挑未染色的点连线,最后再连起来,画线次数不会比第一种方式少。
那么这样每个时刻上色的点的颜色一定相同,所以我们枚举染色状态,染色点以及未染色点确定一条直线然后去跟新 新的状态。这里我们要预处理两点确定的这条直线上存在的点,叉积预处理即可,初始化每一条直线马,每一个点都初始化为1,其余都是无穷大进行转移即可
PII p[N];
int d[N][N];
int f[(1 << 20) + 100]; // f[i]表示在i的二进制状态下,第j为如果为0表示j未染色,为1表示染色了
PII operator-(PII a, PII b) // 重载减号
{
return {a.xx - b.xx, a.yy - b.yy};
}
int cross(PII a, PII b)
{
return a.xx * b.yy - a.yy * b.xx;
}
void solve()
{
int n;
cin >> n;
for (int i = 0; i < n; i++)
cin >> p[i].xx >> p[i].yy;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (i == j)
continue;
for (int k = 0; k < n; k++)
{
if (cross(p[i] - p[j], p[j] - p[k]) == 0) // 叉积为0说明在以i,j的直线上
{
d[i][j] |= 1 << k;
}
}
}
}
memset(f, 0x3f, sizeof f);
for (int i = 0; i < n; i++)
{
f[1 << i] = 1; // 解决可能n=1的情况,n==1的时候,j会被跳过,所以这里先单独初始化一下单独一个点的代价
for (int j = 0; j < n; j++)
{
if (i == j)
continue;
f[d[i][j]] = 1; // 一条直线代价就是1
}
}
for (int i = 0; i < (1 << n); i++)
{
for (int j = 0; j < n; j++)
{
if (i >> j & 1) // 第j个点被染色
{
for (int k = 0; k < n; k++)
{
// 第 k 个点没被染色
// j和k直线 如果被染色过,再染一遍会使答案更大,所以没判
if (k == j)
continue;
f[i | d[j][k]] = min(f[i | d[j][k]], f[i] + 1);
}
}
}
}
cout << f[(1 << n) - 1] << endl;
}
E:漂亮数组
思路:其实贪心的记录一下余数就可以过这题,这里写一下和赛时不一样的dp写法;
表示前从前i个数里选的最大答案
其实思路还是一样的也是记录余数,我们记录一下前缀和的余数,然后map记录上一个余数的下标,然后进行转移即可,注意一下0的判断
int n, k;
int a[N], s[N];
int f[N];
map<int, int> mp;
void solve()
{
cin >> n >> k;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
s[i] = (s[i - 1] + a[i]) % k;
}
mp[0] = 0;
for (int i = 1; i <= n; i++)
{
f[i] = f[i - 1];
if (mp.count(s[i]))
f[i] = max(f[i], f[mp[s[i]]] + 1);
mp[s[i]] = i;
}
cout << f[n] << endl;
}
非dp:
int n, k;
int a[N];
map<int, int> mp;
void solve()
{
int ans = 0;
cin >> n >> k;
for (int i = 1; i <= n; i++)
cin >> a[i];
int s = 0;
for (int i = 1; i <= n; i++)
{
s += a[i];
int t = s % k;
if (t == 0)
{
ans++;
s = 0;
mp.clear();
}
else
{
if (mp[t])
{
ans++;
s = 0;
mp.clear();
}
else
{
mp[t]++;
}
}
}
cout << ans << endl;
}
F:来点每日一题
鸽一下
H、数三角形(hard)
主要用树状数组来计算贡献,时间复杂度n^2logm级别
#include <bits/stdc++.h>
using namespace std;
#define pi acos(-1)
#define xx first
#define yy second
#define endl "\n"
#define lowbit(x) x & (-x)
#define int long long
#define ull unsigned long long
#define pb push_back
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
#define LF(x) fixed << setprecision(x)
#define max(a, b) (((a) > (b)) ? (a) : (b))
#define min(a, b) (((a) < (b)) ? (a) : (b))
#define Yshanqian ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
const int N = 1e6 + 10, M = 3010, inf = 0x3f3f3f3f, mod = 1e9 + 7, P = 13331;
const double eps = 1e-8;
int n, m;
char g[M][M];
int d1[M][M], d2[M][M];
int tr[N];
vector<int> ve[N];
int res;
void add(int x, int c)
{
for (int i = x; i <= m; i += lowbit(i))
tr[i] += c;
}
int query(int x)
{
if (x <= 0)
return 0;
int ans = 0;
for (int i = x; i; i -= lowbit(i))
ans += tr[i];
return ans;
}
void work(int sx, int sy, int ey)
{
int mx = 0;
int i;
for (i = sy; i <= ey; i += 2)
{
// d1[sx][i]就是 (row,i)的最长左腰
int L = i, R = L + d1[sx][i] * 2 - 2; // 最远的右腰
mx = max(mx, R);
ve[R].pb(L); // 到最远的右腰,计算完就撤销这个+1
add(i, 1); // 左腰加1
R = i; // 计算他为右腰时的最左腰
L = R - (d2[sx][i] * 2 - 2);
res += query(i) - query(L - 1); // 求区间和即可,注意这里一个的也加上了,最后减去*的个数即可
for (auto j : ve[i]) // 到哪里清空哪里,一个点可能被加了多次,故一边清空即可
{
add(j, -1);
}
ve[i].clear();
}
for (; i <= mx; i += 2) // 可能有最大的R没有清空到继续// 出去了,没清空到
{
for (auto j : ve[i])
{
add(j, -1);
}
ve[i].clear();
}
// 因为有间隔
mx = 0;
for (i = sy + 1; i <= ey; i += 2)
{
int L = i, R = L + d1[sx][i] * 2 - 2;
mx = max(mx, R);
ve[R].push_back(L);
add(i, 1);
R = i;
L = R - (d2[sx][i] * 2 - 2);
res += query(i) - query(L - 1);
for (auto j : ve[i])
{
add(j, -1);
}
ve[i].clear();
}
for (; i <= mx; i += 2)
{
for (auto j : ve[i])
{
add(j, -1);
}
ve[i].clear();
}
}
void solve()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> g[i] + 1;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
if (g[i][j] == '*')
{
d1[i][j] = d1[i - 1][j + 1] + 1;
d2[i][j] = d2[i - 1][j - 1] + 1;
}
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
if (g[i][j] == '.')
continue;
int k = j;
while (k + 1 <= m && g[i][k + 1] == '*')
++k;
work(i, j, k);
j = k;
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
if (g[i][j] == '*')
res--;
}
cout << res << endl;
}
signed main()
{
Yshanqian;
int T;
T = 1;
// cin >> T;
for (int cases = 1; cases <= T; ++cases)
{
// cout<<"Case #"<<cases<<": ";
solve();
}
return 0;
}