2021-07-12 部分集训题目题解
一时暴力一时爽,一直暴力一直爽(两个亿
染色
Description
Solution
我们考虑先染斜对角线再染行,那么可以发现的是,如果一个斜对角线经过的行都染了色,那选不选这条线都不会有影响。
所以,我们假设一个斜对角线经过行的区间是 \([l_i,r_i]\),那么问题就是选出一些区间,并在这些区间的并中选出一些元素,使得没有一个被选中的区间中每个位置的元素都被选中。可以看出,选元素就是选哪些行被染色。
这个分类讨论然后做个前缀和,复杂度 \(\Theta(n^3)\)。
Code
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define MAXN 2005
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c != ' ' && c != '\n') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> void chkmax (T &a,T b){a = max (a,b);}
template <typename T> void chkmin (T &a,T b){a = min (a,b);}
#define pii pair<int,int>
#define se second
#define fi first
vector <pii> S;
int n,m,mod,pw[MAXN],f[MAXN][MAXN],pre[MAXN][MAXN];
int mul (int a,int b){return 1ll * a * b % mod;}
int dec (int a,int b){return a >= b ? a - b : a + mod - b;}
int add (int a,int b){return a + b >= mod ? a + b - mod : a + b;}
void Add (int &a,int b){a = add (a,b);}
signed main(){
freopen ("coloring.in","r",stdin);
freopen ("coloring.out","w",stdout);
read (n,m,mod),S.resize (n + m + 1);int tot = 0;
for (Int i = 1;i <= n + m - 1;++ i) ++ tot,S[i] = {max (1,i - m + 1),min (n,i)};
pw[0] = 1;for (Int i = 1;i <= max (n,m);++ i) pw[i] = add (pw[i - 1],pw[i - 1]);
for (Int i = 0;i <= n;++ i) pre[0][i] = 1;
for (Int i = 1;i <= tot;++ i){
for (Int j = 0;j <= S[i].se - S[i].fi;++ j){
for (Int k = 0;k < i;++ k){
if (S[i].se - j > S[k].se){
if (S[k].se < S[i].fi) Add (f[i][j],mul (pre[k][n],pw[S[i].se - S[i].fi - j]));
else Add (f[i][j],mul (pre[k][n],pw[S[i].se - S[k].se - j - 1]));
}
else Add (f[i][j],f[k][j + S[k].se - S[i].se]);
}
}
for (Int j = 0;j <= n;++ j) pre[i][j] = add (j ? pre[i][j - 1] : 0,f[i][j]);
}
int ans = 0;
for (Int i = 0;i <= tot;++ i) Add (ans,pre[i][n]);
write (ans),putchar ('\n');
return 0;
}
生命游戏
Description
Solution
对于 \(n=1\) 的情况可以想到的是,每时每刻它存活格子都是斜 \(45^\circ\) 。
对于 \(n=2\) 的情况,可以想到如果两个横纵坐标和的奇偶性相同,那么两者才会产生影响,影响如下:
可以看出,对于时刻 \(t\) ,贡献就是 \((a+t)\times (b+t)\) 的形式。
考虑对此进行拓展,可以想到,从两个点相交到另两个点相交这一段时间的贡献显然就是关于 \(t\) 的三次函数形式。那么我们就可以用扫描线求出一个时刻的贡献,然后暴力插值。
复杂度 \(\Theta(n^4+q\log n)\)。
Code
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define inv2 499122177
#define inv6 166374059
#define mxv 1000000000
#define mod 998244353
#define int long long
#define MAXN 205
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c != ' ' && c != '\n') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> void chkmax (T &a,T b){a = max (a,b);}
template <typename T> void chkmin (T &a,T b){a = min (a,b);}
#define fi first
#define se second
#define pii pair<int,int>
int n,q;
int mul (int a,int b){return 1ll * a * b % mod;}
int dec (int a,int b){return a >= b ? a - b : a + mod - b;}
int add (int a,int b){return a + b >= mod ? a + b - mod : a + b;}
void Add (int &a,int b){a = add (a,b);}
void Sub (int &a,int b){a = dec (a,b);}
vector <pii> vp[2];
struct node{
int x,l,r;
bool t;
};
int a[MAXN],y[MAXN];
int calc (int o,int d){//计算奇偶性为t的点在d时刻的贡献
vector <node> v;
memset (a,0,sizeof (a));
int uni = 0,res = 0,tmp = 0;
for (pii it : vp[o]){
int yl = it.se - (d + 1 >> 1),yr = it.se + (d >> 1) + 1;
y[++ uni] = yl,y[++ uni] = yr,
v.push_back (node{it.fi - (d + 1 >> 1),yl,yr,1}),
v.push_back (node{it.fi + (d >> 1) + 1,yl,yr,0});
}
sort (y + 1,y + uni + 1),uni = unique (y + 1,y + uni + 1) - y - 1,sort (v.begin(),v.end(),[](node s1,node s2){return s1.x < s2.x;});
for (Int i = 0;i < v.size();++ i){
v[i].l = lower_bound (y + 1,y + uni + 1,v[i].l) - y,v[i].r = lower_bound (y + 1,y + uni + 1,v[i].r) - y;
for (Int j = v[i].l;j < v[i].r;++ j)
if (v[i].t){
if (!a[j]) tmp += (y[j + 1] - y[j]);
a[j] ++;
}
else{
a[j] --;
if (!a[j]) tmp -= (y[j + 1] - y[j]);
}
if (i != v.size()) Add (res,mul (tmp,v[i + 1].x - v[i].x));
}
return res;
}
struct fuc{
int y[4];
fuc(){for (Int i = 0;i < 3;++ i) y[i] = 0;}
void makeit (){y[0] = mul (mod - inv6,y[0]),y[1] = mul (inv2,y[1]),y[2] = mul (mod - inv2,y[2]),y[3] = mul (inv6,y[3]);}
int getit (int d){
int pl[4] = {},pr[4] = {},res = 0;
pl[0] = 1;for (Int i = 0;i < 3;++ i) pl[i + 1] = mul (pl[i],dec (d,i));
pr[3] = 1;for (Int i = 3;i > 0;-- i) pr[i - 1] = mul (pr[i],dec (d,i));
for (Int i = 0;i <= 3;++ i) Add (res,mul (mul (pl[i],pr[i]),y[i]));
return res;
}
};
fuc getit (int o,int s,int l,int r){
fuc t;
for (Int i = l;i <= r && i <= l + 3;++ i) Add (s,calc (o,i)),t.y[i - l] = s;
t.makeit();return t;
}
vector <int> tim[2];
vector <fuc> sht[2];
void buildit (int o){
tim[o].push_back (0),tim[o].push_back (mxv + 1);
for (pii it1 : vp[o]) for (pii it2 : vp[o]) tim[o].push_back (max (abs (it1.fi - it2.fi),abs (it1.se - it2.se)));
sort (tim[o].begin(),tim[o].end()),tim[o].erase (unique (tim[o].begin(),tim[o].end()),tim[o].end());
fuc t;for (Int i = 0,lst = 0;i < tim[o].size() - 1;++ i) t = getit (o,lst,tim[o][i],tim[o][i + 1] - 1),sht[o].push_back (t),lst = t.getit (tim[o][i + 1] - 1 - tim[o][i]);
}
int fuckit (int o,int T){
int pos = upper_bound (tim[o].begin(),tim[o].end(),T) - tim[o].begin() - 1;
return sht[o][pos].getit (T - tim[o][pos]);
}
signed main(){
freopen ("lifegame.in","r",stdin);
freopen ("lifegame.out","w",stdout);
read (n,q);
for (Int i = 1;i <= n;++ i){
int x,y;read (x,y);
vp[x + y & 1].push_back ({x + y >> 1,x + mxv - y >> 1});
}
buildit (0),buildit (1);
while (q --> 0){
int T;read (T);
write (add (fuckit (0,T),fuckit (1,T))),putchar ('\n');
}
return 0;
}