前期#6
A
考虑直接数位dp即可,顺带记录往后有多少贡献即可,一些讨论可以具体看代码。
前期#6 A
#include<bits/stdc++.h>
#define ll long long
#define N 305
#define mod 998244353
int f[2][N][N][2];//枚举第几位,往后有几位,已经匹配了几个,是否顶到头
char s[N];
char lim[N];
int nex[N];
int to[N][10];
int m;
int len;
inline void kmp(){
int n = strlen(s + 1);
len = strlen(s + 1);
for(int i = 1;i <= n;++i)
s[i] = s[i] - '0';
int j = 0;
for(int i = 2;i <= n;++i){
while(j && s[j + 1] != s[i])
j = nex[j];
if(s[j + 1] == s[i])
j ++ ;
nex[i] = j;
// std::cout<<i<<" "<<nex[i]<<"\n";
}
for(int i = 0;i <= n;++i){
for(int j = 0;j < 10;++j)
if(s[i + 1] == j)
to[i][j] = i + 1;
else{
int k = i;
while(s[k + 1] != j && k)
k = nex[k];
if(s[k + 1] == j)
to[i][j] = k + 1;
}
// std::cout<<i<<"\n";
// for(int j = 0;j < 10;++j)
// std::cout<<to[i][j]<<" ";
// puts("");
}
}
inline void solve(){
f[0][0][0][1] = 1;
int n = strlen(lim + 1);
for(int i = 1;i <= n;++i)
lim[i] = lim[i] - '0';
for(int i = 1;i <= n;++i){
int now = i & 1;
int las = (i - 1) & 1;
// std::cout<<i<<"\n";
for(int j = 0;j <= n;++j)
for(int k = 0;k <= n;++k)
for(int q = 0;q <= 1;++q)
f[now][j][k][q] = 0;
for(int j = 0;j <= n;++j)
for(int k = 0;k <= n;++k)
for(int q = 0;q <= 1;++q){
if(f[las][j][k][q]){
// std::cout<<"USE"<<" "<<i - 1<<" "<<j<<" "<<k<<" "<<q<<" "<<f[las][j][k][q]<<"\n";
int tj ;
int tk ;
int tq ;
if(q){
for(int c = 0;c < lim[i];++c){
tq = 0;
int t = to[j][c];
if(t == len){
tk = k + 1;
tj = nex[len];
}else{
tk = k;
tj = t;
}
f[now][tj][tk][tq] = (f[now][tj][tk][tq] + f[las][j][k][q]) % mod;
// std::cout<<"CHOOSE "<<c<<" "<<tj<<" "<<tk<<" "<<tq<<"\n";
}
int t = to[j][lim[i]];
tq = 1;
if(t == len){
tk = k + 1 ;
tj = nex[len];
}else{
tk = k;
tj = t;
}
// std::cout<<"CHOOSE "<<(int)lim[i]<<" "<<tj<<" "<<tk<<" "<<tq<<"\n";
f[now][tj][tk][tq] = (f[now][tj][tk][tq] + f[las][j][k][q]) % mod;
}else{
for(int c = 0;c <= 9;++c){
tq = 0;
int t = to[j][c];
if(t == len){
tk = k + 1;
tj = nex[len];
}else{
tk = k;
tj = t;
}
f[now][tj][tk][tq] = (f[now][tj][tk][tq] + f[las][j][k][q]) % mod;
// std::cout<<"CHOOSE "<<c<<" "<<tj<<" "<<tk<<" "<<tq<<"\n";
}
}
}
}
}
ll ans = 0;
for(int j = 0;j <= n;++j)
for(int k = 0;k <= n;++k)
for(int q = 0;q <= 1;++q){
if(k >= m){
ans = (ans + f[n & 1][j][k][q]) % mod;
}
}
std::cout<<ans<<"\n";
}
int main(){
// freopen("p.out","w",stdout);
scanf("%d",&m);
scanf("%s",lim + 1);
scanf("%s",s + 1);
kmp();
solve();
}
C
考虑实际上求的是一个期望方差,方差的话只要维护 平方和 和 和平方即可,后者只要多维护一次和即可,那么考虑维护点集在树上启发式合并可以做到\(nlog\)。
前期#6 C
#include <bits/stdc++.h>
using namespace std;
#define lep(i, l, r) for(int i = (l); i <= (r); i ++)
#define rep(i, l, r) for(int i = (l); i >= (r); i --)
#define Lep(i, l, r) for(int i = (l); i < (r); i ++)
#define Rep(i, l, r) for(int i = (l - 1); i >= (r); i --)
#define debug(...) fprintf (stderr, __VA_ARGS__)
#define pb push_back
#define fi first
#define se second
#define gc getchar
#define pc putchar
using i64 = long long;
using uint = unsigned int;
using ui64 = unsigned long long;
using pii = pair<int, int>;
using vi = vector<int>;
template<typename A, typename B>
inline void Min(A &x, B y) { x = x < y ? x : y; }
template<typename A, typename B>
inline void Max(A &x, B y) { x = x > y ? x : y; }
template<typename T> inline void read(T &x) {
x = 0; char a = gc(); bool f = 0;
for (; ! isdigit(a); a = gc()) if (a == '-') f = 1;
for (; isdigit(a); a = gc()) x = x * 10 + a - '0';
if (f) x = -x;
}
const int P = 1e9 + 7;
inline int mod(int x) { return x + (x >> 31 & P); }
inline void sub(int &x, int y) { x = mod(x - y); }
inline void pls(int &x, int y) { x = mod(x + y - P); }
inline int add(int x, int y) { return mod(x + y - P); }
inline int dec(int x, int y) { return mod(x - y); }
inline int power(int x, int k) {
int res = 1; if (k < 0) k += P - 1;
while (k) { if (k & 1) res = 1ll * res * x % P; x = 1ll * x * x % P; k >>= 1; }
return res;
}
const int N = 1e5 + 10;
int n;
vector<int> e[N];
vector<pii> lk[N];
int a[N];
/*
设 x_i 是某个情况下的和
1. \sum (x_i) ^ 2
2. ((\sum x_i) / m) ^ 2 * m
3. -2 * \sum (x_i * ((\sum x_i) / m))
*/
int f[N], g[N], h[N], q[N], ans[N];
// f 所有情况下平方和
// g 所有情况下和
// q 所有情况下和平方
// h 子树 \prod a_i
void dfs(int x, int fx) {
// h[x] = a[x];
for(auto o : lk[x]) {
pls(f[x], 1ll * o.fi * o.se % P * o.se % P);
pls(g[x], 1ll * o.fi * o.se % P);
pls(q[x], 1ll * o.fi * o.se % P * o.se % P);
pls(h[x], o.fi);
}
for(int y : e[x]) if(y != fx) {
dfs(y, x);
int tf = 0, tg = 0, th = 0, tq = 0;
th = 1ll * h[x] * h[y] % P;
tf = (1ll * f[x] * h[y] % P + 1ll * h[x] * f[y] % P) % P;
tg = (1ll * g[x] * h[y] % P + 1ll * h[x] * g[y] % P) % P;
tq = (1ll * q[x] * h[y] % P + 1ll * h[x] * q[y] % P +
1ll * 2 * g[x] * g[y] % P) % P;
h[x] = th;
f[x] = tf;
g[x] = tg;
q[x] = tq;
}
int m = power(h[x], P - 2);
ans[x] = q[x];
// pls(ans[x], 1ll * q[x] * m % P * m % P);
sub(ans[x], 1ll * g[x] * m % P * g[x] % P);
ans[x] = 1ll * ans[x] * m % P;
}
int main() {
read(n);
for(int i = 2; i <= n; i ++) {
int x, y;
read(x); read(y);
e[x].pb(y);
e[y].pb(x);
}
for(int i = 1; i <= n; i ++) {
read(a[i]);
for(int j = 1; j <= a[i]; j ++) {
int p, x;
read(p); read(x);
lk[i].pb( {p, x} );
}
}
dfs(1, 0);
for(int i = 1; i <= n; i ++) printf("%d\n", ans[i]);
return 0;
}
B
考虑实际上所求的东西可以转化为枚举垂直于直线的方向向量为\((\cos\theta,sin\theta)\)的直线,考虑把圆心映射到线的投影长度,实际上其为\((x,y)(\cos\theta,sin\theta)\)的点积,其切点即为\(\cos\theta * x + sin\theta * y \pm r\),那么距离实际上即找到最大最小的切点的投影差,然后使用辛普森积分法即可。
考虑实际上转化后求的是\(\int_{-\frac{\pi}{2}}^{\frac{\pi}{2}}g(\theta)\,d\theta\)
前期#6 B
#include<bits/stdc++.h>
#define ll long long
#define N 100010
double PI = acos(-1);
int n;
double x[N],y[N],r[N];
std::map<double,double>M;
#define inf 1e60
inline double f(double t){
if(M.count(t))return M[t];
double c = cosl(t),s = sinl(t);
double mx = -inf,mi = inf;
for(int i = 1;i <= n;++i){
double k = x[i] * c + y[i] * s;
mx = std::max(k + r[i],mx);
mi = std::min(k - r[i],mi);
}
return mx - mi;
}
inline double F(double l,double r){
return (r - l) / 6 * (f(l) + f(r) + (f((l + r) / 2)) * 4);
}
inline double dabs(double k){return (k < 0) ? -k : k;}
inline bool cmp(double a,double b,double eps){
return dabs(a - b) < eps || dabs((a - b) / b) < eps;
}
inline double simp(double l,double r,double eps){
double m = (l + r) / 2;
if(cmp(F(l,m) + F(m,r),F(l,r),eps))
return F(l,r);
return simp(l,m,eps / 2) + simp(m,r,eps / 2);
}
int main(){
scanf("%d",&n);
for(int i = 1;i <= n;++i)
scanf("%lf%lf%lf",&x[i],&y[i],&r[i]);
printf("%.10lf\n",simp(-PI/2,PI/2,1e-6));
}
前期#7
A
考虑只需计算\(f_x = \sum_{i\leq k} q_i\),\(q_i\)为恰好最长段为\(i\)。
考虑如何计算\(f_x\),其意义为分为了\(c + 1\)段,\(c = cnt(0)\),其中恰好\(0\)段\(>k\)。
考虑直接使用二项式反演,我们钦定\(x\)段为强制大于,先在其对应段减去\(k + 1\),然后剩下随便选即变成了插板问题。
前期#7 A
#include<bits/stdc++.h>
#define ll long long
#define N 5000005
#define mod 998244353
ll s[N],inv[N];
inline ll qpow(ll a,ll b){
ll ans = 1;
while(b){
if(b & 1)ans = ans * a % mod;
a = a * a % mod;
b >>= 1;
}
return ans;
}
int n,m,k;
int c;
inline ll C(ll x,ll y){
if(x < 0 || y < 0 || y > x)return 0;
return s[x] * inv[y] % mod * inv[x - y] % mod;
}
inline ll find(ll lim){
ll res = 0;
// std::cout<<lim<<std::endl;
for(int i = 0;i <= c + 1;++i){
if(m + c < i * (lim + 1))break;
ll now = C(c + 1,i) * C(m - i * (lim + 1) + c,c) % mod;
// std::cout<<i<<" "<<now<<std::endl;
if(i & 1){
res = (res - now + mod) % mod;
}else{
res = (res + now) % mod;
}
}
return res % mod;
}
int main(){
s[0] = 1;
for(int i = 1;i <= N - 1;++i)
s[i] = s[i - 1] * i % mod;
inv[N - 1] = qpow(s[N - 1],mod - 2);
for(int i = N - 2;i >= 0;--i)
inv[i] = inv[i + 1] * (i + 1) % mod;
// std::cout<<C(4,2)<<"\n";
scanf("%d%d%d",&n,&m,&k);
c = n - m;
ll ans = (find(k) - find(k - 1)) % mod;
std::cout<<(ans < 0 ? ans + mod : ans);
}
B
考虑把所有数都除去\(x\),对其质因子计算。好联通块当且仅当对每个质因子其联通块内都有\(0\)和\(1\)。
那么考虑将边两点的点权异或设为该边的边权,那实际上等同于边权或起来和\(\frac{Y}{X}\)的质因子全集一样。
直接使用\(FWT\)即可。
考虑到直接暴力\(FWT\)有一个\(log\),而我们只要算单点值,我们直接对\(IFWT\)数组操作。
前期#7 B
#include<bits/stdc++.h>
#define ll long long
#define For(i,j,k) for (int i=(int)(j);i<=(int)(k);i++)
using namespace std;
const int N=1005;
const int mo=1000000007;
const int mod=1000000007;
struct edge{int to,next;}e[N*2];
int n,tot,cnt,head[N];
int q[20],S[N],ans;
ll s1,s2,a[N];
void add(int x,int y){e[++tot]=(edge){y,head[x]},head[x]=tot;}
ll f[N][(1<<15)+1];
int tmp[(1<<15)+1];
int bit[(1<<15)+1];
#define M (1<<15)
inline void FWT_or(ll *f,int type){
for(int mid = 1;mid < M;mid <<= 1)
for(int block = mid << 1,j = 0;j < M;j += block)
for(int i = j;i < j + mid;++i)
f[i + mid] = (f[i + mid] + f[i] * type + mod) % mod;
}
void dfs(int x,int fa){
For(i,0,(1<<cnt)-1) f[x][i]=1;
for (int i=head[x];i;i=e[i].next)
if (e[i].to!=fa){
dfs(e[i].to,x);
int diff=S[x]^S[e[i].to];
For(j,0,(1<<cnt)-1) if ((j&diff)==diff)
f[x][j]=1ll*f[x][j]*(1+f[e[i].to][j])%mo;
}
FWT_or(f[x],-1);
ans = (ans + f[x][(1ll << cnt) - 1] % mod) % mod;
FWT_or(f[x],1);
}
int main(){
For(i,0,(1<<15)-1) bit[i]=bit[i/2]+(i&1);
scanf("%d%lld%lld",&n,&s1,&s2); s2/=s1;
For(i,1,n) scanf("%lld",&a[i]),a[i]/=s1;
For(i,2,50) if (s2%i==0) q[cnt++]=i,s2/=i;
For(i,1,n) For(j,0,cnt-1)
if (a[i]%q[j]==0) S[i]|=(1<<j);
For(i,1,n-1){
int x,y;
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
dfs(1,0);
printf("%d\n",ans);
}
C
考虑如果其不对每个点求答案,而是转为对全局求和,那我们可以使用启发式合并更好做。
现在我们只能使用淀粉质。
考虑枚举点后其到分治点的距离为\(x\),其其他子树对其的贡献为\(\sum c_i * w_{x + i}\)
实际上是一个减法卷积,使用NTT即可。
\(O(nlog^2n)\)。
代码暂时咕咕咕。
前期#8
A
考虑到始终把\(1\)作为起点和终点。
那么对于图论路径构造题有一个很经典的图。
前期#8A
#include<bits/stdc++.h>
using namespace std;
#define ri register int
typedef long long ll;
const int maxn=1e5+10;
template<class T>inline bool ckmin(T &x,const T &y){return x>y?x=y,1:0;}
template<class T>inline bool ckmax(T &x,const T &y){return x<y?x=y,1:0;}
template<class T>inline void clear(T *arr,int siz,int val=0){memset(arr,val,sizeof(T)*(siz+1));}
int ans[maxn],n;
int main(){
scanf("%d",&n);
for(ri i=1;i<n;i+=2)ans[i]=(i+1)>>1;
for(ri i=2;i<n;i+=2)ans[i]=n-(i>>1);
for(ri i=1;(i<<1)<n;++i){
for(ri j=1;j<n;++j){
printf("%d ",ans[j]++);
if(ans[j]==n)ans[j]=1;
}
printf("%d\n",n);
}
return 0;
}
B
考虑先构造一个可以大幅度消耗步数的,然后在剩下的小步数暴搜。
后期#1
A
发现其操作为一个数变为其前缀任意异或。
所以是线性基板子。
后期#1A
#include<bits/stdc++.h>
#define ll long long
#define N 100005
#define mod 998244353
int n;
int a[N];
int ans = 1;
int cnt;
int p[N];
void ins(ll num){
for(ll i = 63;i >= 0;i--)
if(num & (1ll << i)){
if(!p[i]){p[i] = num;cnt++;break;}
num ^= p[i];
}
}
int main(){
scanf("%d",&n);
for(int i = 1;i <= n;++i)
scanf("%d",&a[i]);
for(int i = 1;i <= n;++i)
ans = (ans * (1ll << cnt)) % mod,ins(a[i]);
std::cout<<ans<<"\n";
}
后期#2
A
三分板子。
但是考虑到次数限制我第一发写的是黄金分隔比的形式,但是由于这个精度问题,全\(wa\)了。
考虑改为使用斐波那契数列即可
后期#2A
long long guess(int m)
{
ll res = inf;
int l = 1,r = m;
int R = r,L = l;
ll fl = -1,fr = -1;
while(R > L){
if(fl < 0){
l = L + 0.382 * (R - L);
if(!f[l])
f[l] = fl = get(l);
else
fl = f[l];
}else
res = std::min(fl,res);
if(fr < 0){
r = L + 0.618 * (R - L);
if(!f[r])
f[r] = fr = get(r);
else
fr = f[r];
}else
res = std::min(fr,res);
// std::cout<<l<<" "<<r<<" "<<L<<" "<<R<<"\n";
if(fl > fr){
L = l;
l = r;
fl = fr;
fr = -1;
}else{
R = r;
r = l;
fr = fl;
fl = -1;
}
}
// for(int i = L - 5;i <= L + 5;++i)
// res = std::min(res,get(i));
return res;
// return std::min(get(l),std::min(get(r),std::min(get(L),get(R))));
// long long x = get((m + 1) / 2);
// return x;
}
B
考虑实际上是一条路径和这张图的交,转成该图的若干子图的最长链的和即可,其可能存在基环树子图。
后期#3
A
考虑把期望拆开:
\(E = \sum f_{i,j}\)
B
考虑这题求的实际上是最长反链覆盖,AC自动机可以完美的表示偏序可达关系,其根据反图的\(diworth\)定理则有,其等于最长链。
我原本有一个\(log\)的口胡做法,后来发现其也等价于求带权最长链。
串数和串长要注意啊。
点击查看代码
//晦暗的宇宙,我们找不到光,看不见尽头,但我们永远都不会被黑色打倒。——Quinn葵因
#include<bits/stdc++.h>
#define ll long long
#define N 2000005
int n,m;
char a[N];
int root = 0;
struct P{
int len,ch[28];
P(){len = 0;memset(ch,0,sizeof(ch));}
}P[N];
int cnt = 0;
ll w[N];
ll las[N];
inline void insert(){
int u = root;
int len = strlen(a + 1);
for(int i = 1;i <= len;++i){
int c = a[i] - 'a';
// std::cout<<a[i]<<" "<<u<<" "<<P[u].ch[u]<<"\n";
if(!P[u].ch[c])P[u].ch[c] = ++cnt;
P[P[u].ch[c]].len = P[u].len + 1;
las[P[u].ch[c]] = u;
u = P[u].ch[c];
// std::cout<<a[i]<<" "<<u<<"\n";
w[u] ++ ;
}
}
using std::queue;
queue<int>Q;
int fail[N];
inline void build(){
for(int i = 0;i < 26;++i)
if(P[root].ch[i])
fail[P[root].ch[i]] = root,Q.push(P[root].ch[i]);
else
P[root].ch[i] = root;
while(Q.size()){
int u = Q.front();
Q.pop();
// std::cout<<"LINK "<<u<<" "<<fail[u]<<"\n";
for(int i = 0;i < 26;++i){
if(P[u].ch[i]){
fail[P[u].ch[i]] = P[fail[u]].ch[i];
Q.push(P[u].ch[i]);
}else{
P[u].ch[i] = P[fail[u]].ch[i];
}
}
}
}
ll f[N];
ll t[N],r[N];
inline void top(){
for(int i = 1;i <= cnt;++i)t[P[i].len] ++ ;
for(int i = 1;i <= cnt;++i)t[i] += t[i - 1];
for(int i = 1;i <= cnt;++i)r[t[P[i].len] -- ] = i;
for(int i = 1;i <= cnt;++i)
f[i] = w[i];
for(int i = cnt;i >= 1;--i){
int u = r[i];
// std::cout<<u<<" "<<P[u].len<<" "<<f[u]<<"\n";
f[fail[u]] = std::max(f[fail[u]],w[fail[u]] + f[u]);
f[las[u]] = std::max(f[las[u]],w[las[u]] + f[u]);
}
std::cout<<f[0]<<"\n";
}
int main(){
scanf("%d",&n);
for(int i = 1;i <= n;++i){
scanf("%s",a + 1);
insert();
}
build();
top();
}
后期#4
A
考虑偶数实际上就是异或和为0.
但是由于不同数之间会干扰。
我们对每个数随机\(log\)个权值维,每一维都查询就行了。
后期#4A
#include<bits/stdc++.h>
#define ll long long
#define N 1000005
#define ull unsigned ll
ull val[N][15];
ull a[N][15];
ull key[N];
int n,q;
std::unordered_map<ull,int>M;
ull k[15];
inline void init(){
for(int i = 1;i <= 1e6;++i)
for(int j = 1;j <= 10;++j)
val[i][j] = rand() * rand();
for(int i = 1;i <= 10;++i)
k[i] = rand() * rand();
}
int B;
int in[N];
struct P{int l,r,w;}Q[N];
bool operator < (P A,P B){
return (in[A.l] != in[B.l] ? in[A.l] < in[B.l] : (in[A.l] & 1 ? A.r < B.r : A.r > B.r));
}
int ans = 0;
inline void del(int x){
M[key[x]] -- ;
ans -= M[key[x]];
}
inline void ins(int x){
ans += M[key[x]];
M[key[x]] ++ ;
}
ll fans[N];
int main(){
std::srand(time(0));
init();
scanf("%d",&n);
B = sqrt(n);
for(int i = 1;i <= n;++i)
in[i] = (i - 1) / B + 1;
for(int i = 1;i <= n;++i){
int x;
scanf("%d",&x);
for(int j = 1;j <= 10;++j)
a[i][j] = val[x][j];
}
for(int i = 1;i <= n;++i){
for(int j = 1;j <= 10;++j)
a[i][j] = a[i - 1][j] ^ a[i][j];
for(int j = 1;j <= 10;++j)
key[i] = key[i] + a[i][j] * k[j];
}
scanf("%d",&q);
for(int i = 1;i <= q;++i){
scanf("%d%d",&Q[i].l,&Q[i].r);
Q[i].l -- ;
Q[i].w = i;
}
std::sort(Q + 1,Q + q + 1);
int l = 1,r = 0;
for(int i = 1;i <= q;++i){
while(r < Q[i].r)ins(++ r);
while(r > Q[i].r)del(r --);
while(l < Q[i].l)del(l ++);
while(l > Q[i].l)ins(-- l);
fans[Q[i].w] = ans;
}
for(int i = 1;i <= q;++i)
std::cout<<fans[i]<<"\n";
}
B
考虑从中间劈开维护两个栈,并按层数维护\(dp\)。
考虑我们当一个栈空时,从另外一边拉一半给他并对两个堆重构。
知这个操作均摊\(O(k)\)次。
复杂度\(O(qk)\).
后期#4B
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5005,M=998244353;
int Q,m,x,y,opt;
inline void reduce(int &x){
if (x>=M)x-=M;
}
struct Stack{
int dp[N][N],val[N],tot;
void add(int x){
tot++;
val[tot]=x;int t=(m-x)%m;
for (int i=0;i<m;i++)reduce(dp[tot][i]=dp[tot-1][i]+dp[tot-1][(i+t)%m]);
}
}A,B;
int main(){
scanf("%d%d",&Q,&m);
A.dp[0][0]=B.dp[0][0]=1;
while (Q--){
scanf("%d",&opt);
if (opt==1){
scanf("%d",&x);
A.add(x);
}
if (opt==2){
scanf("%d",&x);
B.add(x);
}
if (opt==3){
if (A.tot==0){
int len=(B.tot+1)/2,t=B.tot;B.tot=0;
for (int i=len;i;i--)A.add(B.val[i]);
for (int i=len+1;i<=t;i++)B.add(B.val[i]);
}
A.tot--;
}
if (opt==4){
if (B.tot==0){
int len=(A.tot+1)/2,t=A.tot;A.tot=0;
for (int i=len;i;i--)B.add(A.val[i]);
for (int i=len+1;i<=t;i++)A.add(A.val[i]);
}
B.tot--;
}
if (opt==5){
scanf("%d%d",&x,&y);
int ans=0,s=0;
for (int i=x;i<=y;i++)reduce(s+=A.dp[A.tot][i]);
ans=(ll)s*B.dp[B.tot][0]%M;
for (int i=1;i<m;i++){
reduce(s+=M-A.dp[A.tot][(y-i+1+m)%m]);
reduce(s+=A.dp[A.tot][(x-i+m)%m]);
ans=(ans+(ll)s*B.dp[B.tot][i])%M;
}
printf("%d\n",ans);
}
}
return 0;
}
后期#5
A
考虑直接记奇数次数质因子的乘积即可。
后期#5 A
bool vis[32000];
int pr[32000],cnt=0;
signed main()
{
int n=read();
set<int> s;
for(int i=2; i<32000; ++i)
{
if(!vis[i]) pr[++cnt]=i;
for(int j=1; j<=cnt&&pr[j]*i<32000; ++j)
{
vis[pr[j]*i]=1;
if(i%pr[j]==0) break;
}
}
for(int i=1; i<=n; ++i)
{
int x=read();
for(int j=1; j<=cnt&&pr[j]*pr[j]<=x; ++j)
while(x%(pr[j]*pr[j])==0)
x/=pr[j]*pr[j];
s.insert(x);
}
printf("%d\n",(int)s.size());
return 0;
}
后期#6
A
B
向量空间基底板子题,我的做法好像和其他人不一样。
后期#6 B
#include<bits/stdc++.h>
#define ll long long
#define N 105
#define int ll
int m = 6;
using std::vector;
struct P{
int a[N];
inline void init(){for(int i = 0;i < m;++i)a[i] = 0;}
}T[N];
P operator + (P A,P B){
P res;
res.init();
for(int i = 0;i < m;++i)
res.a[i] = A.a[i] + B.a[i];
return res;
}
P operator - (P A,P B){
P res;
res.init();
for(int i = 0;i < m;++i)
res.a[i] = A.a[i] - B.a[i];
return res;
}
P operator * (P A,int x){
P res;
res.init();
for(int i = 0;i < m;++i)
res.a[i] = A.a[i] * x;
return res;
}
inline int lcm(int a,int b){
return a / std::__gcd(a,b) * b;
}
struct LB{
P b[N];
inline void clear(){
for(int i = 0;i < m;++i)
b[i].init();
}
inline bool insert(P c){
for(int i = m - 1;i >= 0;--i){
if(c.a[i] == 0)continue;
if(b[i].a[i] == 0){
b[i] = c;
return true;
}
int L = lcm(abs(c.a[i]),abs(b[i].a[i]));
if(c.a[i] * b[i].a[i] < 0) L = - L;
int tx = L / c.a[i];
int ty = L / b[i].a[i];
c = c * tx - b[i] * ty;
}
return false;
}
}B;
int n;
int ti;
signed main(){
scanf("%lld",&ti);
while(ti -- ){
B.clear();
scanf("%lld",&n);
for(int i = 1;i <= n;++i)
for(int j = 0;j < 6;++j)
scanf("%lld",&T[i].a[j]);
int cnt = 0;
for(int i = 1;i <= n;++i){
if(B.insert(T[i]))
cnt ++ ;
}
std::cout<<cnt<<"\n";
}
}
C
考虑实际只有两种情况,相邻的是祖先关系,和非祖先关系
实际上其都是给定了区间dfn序不能共同存在的条件。
那只要扫描线并使用线段树维护最小值即可。
后期#6 C
#include <bits/stdc++.h>
#define N 300005
#define ll long long
#define getchar nc
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read()
{
register int x=0,f=1;register char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x*f;
}
inline void write(register int x)
{
if(!x)putchar('0');if(x<0)x=-x,putchar('-');
static int sta[20];register int tot=0;
while(x)sta[tot++]=x%10,x/=10;
while(tot)putchar(sta[--tot]+48);
}
struct edge{
int to,nxt;
}e[N<<1];
int head[N],cnt;
void add(int u,int v){
e[++cnt]=(edge){v,head[u]}; head[u]=cnt;
}
int n,dfn[N],tim,siz[N],fa[N][20],dep[N];
void dfs(int x,int f){
siz[x]=1; dfn[x]=++tim;
fa[x][0]=f,dep[x]=dep[f]+1;
for(int i=1;i<=18;++i) fa[x][i]=fa[fa[x][i-1]][i-1];
for(int i=head[x];i;i=e[i].nxt){
int v=e[i].to; if(v==f) continue;
dfs(v,x);
siz[x]+=siz[v];
}
}
int get(int x,int y){
for(int i=18;i>=0;--i) if(dep[y]-(1<<i)>dep[x]) y=fa[y][i];
return y;
}
struct node{
int l,r,v;
};
vector<node> v[N];
void ban(int a,int b,int c,int d){
//cerr<<a<<" "<<b<<" "<<c<<" "<<d<<endl;
v[a].push_back((node){c,d,1});
v[b+1].push_back((node){c,d,-1});
v[c].push_back((node){a,b,1});
v[d+1].push_back((node){a,b,-1});
}
struct tnode{
int mn,cnt,tg;
}tr[N<<3];
void build(int x,int l,int r){
tr[x].mn=0,tr[x].cnt=r-l+1;
if(l==r) return;
int mid=l+r>>1;
build(x<<1,l,mid),build(x<<1|1,mid+1,r);
}
void pushdown(int x){
if(tr[x].tg){
tr[x<<1].mn+=tr[x].tg; tr[x<<1].tg+=tr[x].tg;
tr[x<<1|1].mn+=tr[x].tg; tr[x<<1|1].tg+=tr[x].tg;
tr[x].tg=0;
}
}
void modify(int x,int l,int r,int L,int R,int v){
if(L<=l&&r<=R){
tr[x].mn+=v; tr[x].tg+=v;
return;
}
pushdown(x);
int mid=l+r>>1;
if(L<=mid) modify(x<<1,l,mid,L,R,v);
if(R>mid) modify(x<<1|1,mid+1,r,L,R,v);
tr[x].mn=tr[x<<1].mn,tr[x].cnt=tr[x<<1].cnt;
if(tr[x<<1|1].mn<tr[x].mn) tr[x].mn=tr[x<<1|1].mn,tr[x].cnt=tr[x<<1|1].cnt;
else if(tr[x<<1|1].mn==tr[x].mn) tr[x].cnt+=tr[x<<1|1].cnt;
}
ll ans=0;
int main()
{
n=read();
for(int i=1;i<n;++i){
int u=read(),v=read();
add(u,v),add(v,u);
}
dfs(1,0);
for(int i=1;i<n;++i){
int x=i,y=i+1;
if(dfn[x]>dfn[y]) swap(x,y);
if(dfn[x]+siz[x]-1>=dfn[y]){
int p=get(x,y);
ban(1,dfn[p]-1,dfn[y],dfn[y]+siz[y]-1);
if(dfn[p]+siz[p]<=n) ban(dfn[p]+siz[p],n,dfn[y],dfn[y]+siz[y]-1);
}else{
ban(dfn[x],dfn[x]+siz[x]-1,dfn[y],dfn[y]+siz[y]-1);
}
}
build(1,1,n);
for(int i=1;i<=n;++i){
for(int j=0;j<(int)v[i].size();++j){
modify(1,1,n,v[i][j].l,v[i][j].r,v[i][j].v);
// cerr<<v[i][j].l<<" "<<v[i][j].r<<" "<<v[i][j].v<<endl;
}
ans+=tr[1].cnt;
//cerr<<tr[1].cnt<<endl;
// cerr<<tr[1].mn<<endl;
}
printf("%lld\n",(ans+n)/2);
return 0;
}
后期#7
A
考虑到可猜想答案为\(\frac{1}{2}\sum_x\sum_i|a_i - b_i|\)
同时发现其有差分性质。
那么直接求\([1,R]\)的答案即可。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define FOR(i,a,b) for(int i=a;i<=b;i++)
const int N=1e5+10;
char a[N],b[N];
vector<int>q[N];
long long sum,ans[N],d[N<<1];
int n,m,p,s[N],L[N],R[N],tag[N];
int main(){
scanf("%d%d%s%s",&n,&m,a+1,b+1);
FOR(i,1,m)scanf("%d%d",L+i,R+i);
FOR(ch,'A','Z'){
FOR(i,1,n)s[i]=s[i-1]+(a[i]==ch)-(b[i]==ch);
FOR(i,1,m)tag[i]|=s[L[i]-1]!=s[R[i]];
}
FOR(i,1,m)if(!tag[i])q[L[i]-1].push_back(-i),q[R[i]].push_back(i);
FOR(ch,'A','Z'){
FOR(i,1,n){
if(a[i]==ch)d[N+p+1]+=i<<1,sum+=d[++p+N]-i;
if(b[i]==ch)sum-=d[p--+N]-i,d[N+p+1]-=i<<1;
for(int v:q[i])v>0?ans[v]+=sum:ans[-v]-=sum;
}
memset(d+N-n,0,n<<4),sum=p=0;
}
FOR(i,1,m)cout<<(ans[i]>>1|-tag[i])<<'\n';
return 0;
}
B
和很naive。
与和或的话实际上是求\(1/0\)四联通块的矩形个数。
异或的话,考虑拆位,枚举上下边界,然后\(bitset\)优化即可。
点击查看代码
#include<bits/stdc++.h>
#define MAXN 110000
#define int long long
#define MOD 998244353
using namespace std;
int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if (ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x*f;
}
int n,m;
int a[MAXN],b[MAXN];
int ans1,ans2,ans3,ans4;
inline int num(int x,int y){
return (x-1)*m+y;
}
void pls(int &x,int y){
x+=y;
(x>=MOD)?x-=MOD:0;
}
int u[MAXN],sta[MAXN],top,c[MAXN];
int ask(int t){
memset(u,0,sizeof(u));
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++) {
for(int j=1;j<=m;j++)
u[j]=(b[num(i,j)]==t)?u[j]+1:0;
top=0;
sta[++top]=0;
for(int j=1;j<=m+1;j++) {
while(u[sta[top]]>u[j]) {
++c[num(max(u[sta[top-1]], u[j])+1,j-sta[top-1]-1)];
--c[num(u[sta[top]]+1,j-sta[top-1]-1)];
--top;
}
while(top&&u[sta[top]]==u[j]) --top;
sta[++top]=j;
}
}
for(int i=2;i<=n;i++)
for(int j=1;j<=m;j++)
pls(c[num(i,j)],c[num(i-1,j)]);
for(int i=1;i<=n;i++) {
for(int j=m-1;j;--j)
pls(c[num(i,j)],c[num(i,j+1)]);
for(int j=m-1;j;--j)
pls(c[num(i,j)],c[num(i,j+1)]);
}
int res=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
pls(res,c[num(i,j)]);
return res;
}
int t[MAXN];
signed main(){
// freopen("b3.in","r",stdin);
n=read(),m=read();
int tmp=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
a[num(i,j)]=read(),pls(ans1,a[num(i,j)]*i%MOD*(n-i+1)%MOD*j%MOD*(m-j+1)%MOD),pls(tmp,i*j%MOD);
for(int s=0;s<=31;s++){
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
b[num(i,j)]=(a[num(i,j)]>>s)&1;
pls(ans2,ask(1)*(1<<s)%MOD);
}
for(int s=0;s<=31;s++){
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
b[num(i,j)]=(a[num(i,j)]>>s)&1;
pls(ans3,((((tmp-ask(0))%MOD+MOD)%MOD)<<s)%MOD);
}
for(int i=1;i<=n;i++)
for(int j=2;j<=m;j++)
a[num(i,j)]^=a[num(i,j-1)];
for(int s=0;s<=31;s++){
memset(c,0,sizeof(c));
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
c[num(i,(j-1)>>6)]+=((a[num(i,j)]>>s)&1)<<((j-1)&63);
for(int l=1;l<=n;l++){
for(int i=0;i<=(m>>6);i++)
t[i]=0;
for(int r=l;r<=n;r++){
int res=0;
for(int i=0;i<=(m>>6);i++)
res+=__builtin_popcountll(t[i]^=c[num(r,i)]);
pls(ans4,(res*(m+1-res)%MOD<<s)%MOD);
}
}
}
printf("%lld %lld %lld %lld\n",ans1,ans2,ans3,ans4);
return 0;
}
C
考虑我们如何在序列上做这个问题。
我们直接对序列分治,然后求出前缀后缀背包合并即可。
那么考虑把他改到树上分治即可。
考虑把询问\((x,y,l,r)\)打在\(x,y\)在点分树上的\(lca\)里。
然后在处理\(lca\)时合并其即可。
合并时两个背包考虑使用单调队列即可实现复杂度\(O((n + q)mlogn)\)
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define N 500005
#define M 505
int c[N],w[N];
int n,m,q;
using std::vector;
vector<int>e[N],t[N];//边 和 task
struct P{
int x,y,l,r;
inline void in(){scanf("%d%d%d%d",&x,&y,&l,&r);}
}a[N];
#define inf 0x3f3f3f3f
int sum,minn = inf;
int root;
int vis[N];
int siz[N];
int F[N][30];
int dep[N];
int tag[N];
int pre[N];
int cnt;
inline int lca(int x,int y){
++ cnt;
while(1){
if(x && tag[x] == cnt)
return x;
else
tag[x] = cnt;
// std::cout<<x<<" "<<y<<"\n";
x = pre[x],std::swap(x,y);
}
}
//LCA____________________________
inline void getrt(int u,int fa = 0){
int maxn = 1;
siz[u] = 1;
for(auto v : e[u]){
if(v == fa || vis[v])continue;
getrt(v,u);
maxn = std::max(maxn,siz[v]);
siz[u] = siz[u] + siz[v];
}
maxn = std::max(maxn,sum - siz[u]);
if(maxn < minn)
minn = maxn,root = u;
// std::cout<<"SIZ "<<u<<" "<<siz[u]<<"\n";
}//找重心
inline void build(int u){
vis[u] = 1,getrt(u);
for(int v : e[u]){
if(vis[v])continue;
sum = siz[v],minn = inf,getrt(v,u),pre[root] = u,build(root);
// std::cout<<u<<"fucked"<<v<<" "<<root<<"\n";
}
}
inline void init(){
sum = n,minn = inf,getrt(1);
build(root);
for(int i = 1;i <= n;++i)
vis[i] = 0;
for(int i = 1;i <= q;++i){
t[lca(a[i].x,a[i].y)].push_back(i);
}
}//处理LCA 和 task 标记
ll f[N][M];
inline void fdp(int u,int fa){
int p;
siz[u] = 1;
for(int i = 0;i < m;++i){
p = (i + c[u]) % m;
f[u][p] = std::max(f[fa][i] + w[u],f[fa][p]);
}
for(int v : e[u]){
if(v == fa || vis[v])continue;
fdp(v,u);
siz[u] += siz[v];
}
}
inline ll merge(ll *a,ll *b,int l,int r,int c,int w){
static ll tmp[N],q[N];
ll res = -inf;
int li = 0,ri = -1;
int len = r - l + 1;
for(int i = 0;i < m;++i){
int p = (i + c) % m ;
tmp[p] = tmp[p + m] = std::max(a[i] + w,a[p]);
}
// std::cout<<"Merge "<<"\n";
// for(int i = 0;i < m;++i)
// std::cout<<a[i]<<" ";
// puts("");
// for(int i = 0;i < m;++i)
// std::cout<<b[i]<<" ";
// puts("");
// for(int i = 0;i < 2 * m;++i)
// std::cout<<tmp[i]<<" ";
// puts("");
for(int i = 0;i < 2 * m;++i){
li = (li + (li <= ri && q[li] <= (i - len)));
while(li <= ri && tmp[q[ri]] <= tmp[i])ri -- ;
q[++ri] = i;
// std::cout<<li<<" "<<ri<<"\n";
if(i >= len - 1){
res = std::max(res,b[(r + 2 * m - i) % m] + tmp[q[li]]);
// std::cout<<q[li]<<"\n";
}
}
return res;
}
ll ans[N];
inline void solve(int u){
// std::cout<<u<<"\n";
vis[u] = 1;
f[u][0] = 0;
for(int i = 1;i <= m - 1;++i)
f[u][i] = -inf;
for(int v : e[u]){
if(vis[v])continue;
fdp(v,u);
}
for(int v : t[u]){
ans[v] = merge(f[a[v].x],f[a[v].y],a[v].l,a[v].r,c[u],w[u]);
}
for(int v : e[u]){
if(vis[v])continue;
sum = siz[v],minn = inf,getrt(v,u),solve(root);
// std::cout<<u<<"->"<<v<<" "<<root<<"\n";
}
}
inline void work(){
sum = n,minn = inf,getrt(1),solve(root);
for(int i = 1;i <= q;++i)
std::cout<<std::max(-1ll,ans[i])<<"\n";
}
int main(){
// freopen("q.in","r",stdin);
// freopen("table1.out","w",stdout);
scanf("%d%d%d",&n,&m,&q);
for(int i = 1;i <= n;++i)
scanf("%d",&c[i]);
for(int i = 1;i <= n;++i)
scanf("%d",&w[i]);
for(int i = 2;i <= n;++i){
int x,y;
scanf("%d%d",&x,&y);
e[x].push_back(y);
e[y].push_back(x);
}
for(int i = 1;i <= q;++i){
a[i].in();
}
init();
work();
}
后期#8
A
考虑列出方程\(x_i = a_{i - 1}x_{i - 1} + (1 - a_{i + 2})x_{i + 2}\)
考虑化简其可得\(x_{i + 2} = \frac{x_i - a_{i - 1}x_{i - 1}}{1 - a_{i + 2}}\)
即\(x_i = \frac{x_{i - 2} - a_{i - 3}x_{i - 3}}{1 - a_i}\)
可以看到\(x_i\)只和其前面三位有关。
我们考虑按照这个柿子把\(x_{4....n}\)都写作\(c_1x_1 + c_2x_2 + c_3x_3\)
注意到我们写出\(x_i\)的表达式时,使用的是\(x_{i - 2} = ....\)的柿子,那么我们还有\(x_{i - 1} = ...,x_{i} = ...,x_{1} = ...\)的柿子,另外我们还需要保证\(\sum x_i = 1\),那么可以利用这些柿子写出矩阵,高斯消元求出\(x_1,x_2,x_3\),回带即可。
点击查看代码
// code by fhq_treap
#include<bits/stdc++.h>
#define ll long long
#define N 400005
inline ll read(){
char C=getchar();
ll A=0 , F=1;
while(('0' > C || C > '9') && (C != '-')) C=getchar();
if(C == '-') F=-1 , C=getchar();
while('0' <= C && C <= '9') A=(A << 1)+(A << 3)+(C - 48) , C=getchar();
return A*F;
}
template <typename T>
void write(T x)
{
if(x < 0) {
putchar('-');
x = -x;
}
if(x > 9)
write(x/10);
putchar(x % 10 + '0');
return;
}
int n;
ll a[N];
#define mod 998244353
inline ll qpow(ll a,ll b){
ll res = 1;
while(b){
if(b & 1)res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
struct P{ll x,y,z;P(){x = y = z = 0;}}E[N];
P operator + (P a,P b){
P res;
res.x = a.x + b.x;
res.y = a.y + b.y;
res.z = a.z + b.z;
res.x = (res.x + mod) % mod;
res.y = (res.y + mod) % mod;
res.z = (res.z + mod) % mod;
return res;
}
P operator * (P a,ll x){
P res;
res.x = a.x * x % mod;
res.y = a.y * x % mod;
res.z = a.z * x % mod;
res.x = (res.x + mod) % mod;
res.y = (res.y + mod) % mod;
res.z = (res.z + mod) % mod;
return res;
}
P operator * (int x,P a){
P res;
res.x = a.x * x % mod;
res.y = a.y * x % mod;
res.z = a.z * x % mod;
res.x = (res.x + mod) % mod;
res.y = (res.y + mod) % mod;
res.z = (res.z + mod) % mod;
return res;
}
//主元x_1,x_2,x_3
/*
x_i = a_{i - 1}x_{i - 1} + (1 - a_{i + 2})x_{i + 2}
->
x_{i - 2} = a_{i - 3}x_{i - 3} + (1 - a_{i})x_{i}
->
x_i = (x_{i - 2} - a_{i - 3} * x_{i - 3}) / 1 - a_{i}
*/
int t[10][10];
inline void guass(int n){
for(int i = 1;i <= n;++i){
int pos = i;
for(int j = i;j <= n;++j)
if(t[j][i]){
pos = j;
break;
}
if(pos ^ i)std::swap(t[i],t[pos]);
int w = qpow(t[i][i],mod - 2);
for(int j = 1;j <= n;++j){
if(j == i)continue;
int ti = 1ll * t[j][i] * w % mod;
for(int k = i;k <= n + 1;++k)
t[j][k] = ((t[j][k] - 1ll * ti * t[i][k] % mod) % mod + mod) % mod;
}
}
for(int i = 1;i <= n;++i)
t[i][n + 1] = 1ll * (1ll * t[i][n + 1] * (mod - 1) % mod * qpow(t[i][i],mod - 2)) % mod;
// for(int i = 1;i <= 3;++i,puts(""))
// for(int j = 1;j <= 4;++j)
// std::cout<<t[i][j]<<" ";
// puts("");
}
inline int w(int x){return (x > n) ? (n - (x - n)) : x;}
ll ans[N];
int main(){
scanf("%d",&n);
for(int i = 1;i <= n;++i)
scanf("%lld",&a[i]),a[i] = a[i] * qpow(1000000,mod - 2) % mod;
// for(int i = 1;i <= n;++i)
// std::cout<<a[i]<<" ";
// puts("");
E[1].x = 1;
E[2].y = 1;
E[3].z = 1;
for(int i = 4;i <= n;++i){
E[i] = (E[i - 2] + E[i - 3] * (-1 * a[i - 3])) * qpow((1 - a[i] + mod) % mod,mod - 2);
// std::cout<<E[i].x<<" "<<E[i].y<<" "<<E[i].z<<"\n";
}
/*
x_1 = a_{x_n}x_{n} + (1 - a_{3})x_3
x_2 = a_{x_1}x_1 + (1 - a[4])x_4
x_3 = a_{x_2}x_2 + (1 - a_5)x_5
*/
E[n + 1] = a[n] * E[n] + ((1 - a[3] + mod) % mod) * E[3];//x_1 =
// std::cout<<w(2 + 2)<<"\n";
E[n + 2] = a[n - 1] * E[n - 1] + ((1 - a[2] + mod) % mod) * E[2] + -1 * E[n];//x_n = a_{n - 1}x_n - 1 + (1 - a_2) x_2
// std::cout<<E[n + 2].x<<" "<<E[n + 2].y<<" "<<E[n + 2].z<<"\n";
for(int i = 1;i <= n;++i)
E[n + 4] = E[n + 4] + E[i];
t[1][1] = E[n + 4].x,t[1][2] = E[n + 4].y,t[1][3] = E[n + 4].z,t[1][4] = 998244352;
t[2][1] = (E[n + 1].x - 1 + mod) % mod,t[2][2] = E[n + 1].y,t[2][3] = E[n + 1].z;
t[3][1] = E[n + 2].x,t[3][2] = E[n + 2].y,t[3][3] = E[n + 2].z;
// puts("");
// for(int i = 1;i <= 3;++i,puts(""))
// for(int j = 1;j <= 3;++j)
// std::cout<<t[i][j]<<" ";
// puts("");
guass(3);
ans[1] = t[1][4];
ans[2] = t[2][4];
ans[3] = t[3][4];
// ans[1] = 927711912;
// ans[2] = 983534457;
// ans[3] = 73291188;
// std::cout<<(E[n + 2].x * ans[1] % mod + E[n + 2].y * ans[2] % mod + E[n + 2].z * ans[3] % mod) % mod<<"\n";
// std::cout<<(E[n + 1].x * ans[1] % mod + E[n + 1].y * ans[2] % mod + E[n + 1].z * ans[3] % mod) % mod<<"\n";
// std::cout<<(E[n + 4].x * ans[1] % mod + E[n + 4].y * ans[2] % mod + E[n + 4].z * ans[3] % mod) % mod<<"\n";
// std::cout<<(ans[1] * E[4].x % mod + ans[2] * E[4].y % mod) % mod<<"\n";
// std::cout<<(ans[2] - a[1] * ans[1] % mod + mod) % mod * qpow((1 - a[4] + mod) % mod,mod - 2) % mod<<"\n";
for(int i = 4;i <= n;++i)
ans[i] = (ans[1] * E[i].x % mod + ans[2] * E[i].y % mod + ans[3] * E[i].z % mod) % mod;
for(int i = 1;i <= n;++i)
std::cout<<ans[i]<<"\n";
}
B
考虑到\(w\)不减。
那么我们实际上修改\(u\)时,只需要考虑和\(u\)有关的点对即可。
考虑\(bfs\)序,那么\(u\)的孙子以及\(u\)的父亲的儿子的范围均连续。
使用线段树维护最大次大值即可。
点击查看代码
#include <bits/stdc++.h>
const int MAXN = 1000010;
const int mod = 998244353;
typedef long long LL;
int mul(int a, int b) { return (LL) a * b % mod; }
void reduce(int & x) { x += x >> 31 & mod; }
void fma(int & x, int y, int z) {
x = ((LL) y * z + x) % mod;
}
void gma(LL & x, LL y) { x < y ? x = y : 0; }
int head[MAXN], nxt[MAXN << 1], to[MAXN << 1], tot;
int xs[MAXN], ys[MAXN], n;
void adde(int b, int e) {
nxt[++tot] = head[b]; to[head[b] = tot] = e;
nxt[++tot] = head[e]; to[head[e] = tot] = b;
}
int tree[MAXN << 2], ma[MAXN << 2];
void update(int u) {
reduce(tree[u] = tree[u << 1] + tree[u << 1 | 1] - mod);
ma[u] = std::max(ma[u << 1], ma[u << 1 | 1]);
}
void mdf(int u, int l, int r, int tar, int v) {
if (l == r) return (void) (tree[u] = ma[u] = v);
int mid = (l + r) >> 1;
if (tar <= mid) mdf(u << 1, l, mid, tar, v);
else mdf(u << 1 | 1, mid + 1, r, tar, v);
update(u);
}
int qryv(int u, int l, int r, int L, int R) {
if (L <= l && r <= R) return tree[u];
int res = 0, mid = (l + r) >> 1;
if (L <= mid) res = qryv(u << 1, l, mid, L, R);
if (mid < R) reduce(res += qryv(u << 1 | 1, mid + 1, r, L, R) - mod);
return res;
}
int qryma(int u, int l, int r, int L, int R) {
if (L <= l && r <= R) return ma[u];
int res = 0, mid = (l + r) >> 1;
if (L <= mid) res = qryma(u << 1, l, mid, L, R);
if (mid < R) res = std::max(res, qryma(u << 1 | 1, mid + 1, r, L, R));
return res;
}
int bfn[MAXN], bzin[MAXN], bzout[MAXN], t0t, bdin[MAXN], bdout[MAXN];
int fa[MAXN];
void bfs() {
std::queue<int> q;
q.push(1);
while (!q.empty()) {
int t = q.front(); q.pop();
bfn[t] = ++t0t;
int as = fa[fa[t]];
if (as) {
if (!bzin[as]) bzin[as] = bfn[t];
bzout[as] = bfn[t];
}
as = fa[t];
if (as) {
if (!bdin[as]) bdin[as] = bfn[t];
bdout[as] = bfn[t];
}
for (int i = head[t]; i; i = nxt[i])
if (to[i] != fa[t]) {
q.push(to[i]);
fa[to[i]] = t;
}
}
}
int vt[MAXN], val[MAXN];
void build(int u, int l, int r) {
if (l == r) return (void) (ma[u] = tree[u] = vt[l]);
int mid = (l + r) >> 1;
build(u << 1, l, mid); build(u << 1 | 1, mid + 1, r);
update(u);
}
LL v1; int v2;
namespace precalc{
int fir[MAXN], sec[MAXN], sum[MAXN], deg[MAXN];
void update(int u, int v) {
if (v >= fir[u]) sec[u] = fir[u], fir[u] = v;
else if (v > sec[u]) sec[u] = v;
reduce(sum[u] += v - mod); ++deg[u];
}
void run() {
for (register int i = 1; i < n; ++i) {
update(xs[i], val[ys[i]]);
update(ys[i], val[xs[i]]);
}
for (register int i = 1; i <= n; ++i) {
gma(v1, (LL) fir[i] * sec[i]);
fma(v2, sum[i], sum[i]);
fma(v2, mul(val[i], val[i]), mod - deg[i]);
}
}
}
void build() {
bfs();
for (int i = 1; i <= n; ++i) vt[bfn[i]] = val[i];
build(1, 1, n);
precalc::run();
}
int main() {
std::ios_base::sync_with_stdio(false), std::cin.tie(0);
int Q; std::cin >> n >> Q;
for (int i = 1; i < n; ++i) {
std::cin >> xs[i] >> ys[i];
adde(xs[i], ys[i]);
}
for (int i = 1; i <= n; ++i)
std::cin >> val[i];
build();
while (Q --> 0) {
int opt, v, w;
std::cin >> opt;
if (opt == 1)
std::cout << v1 << ' ' << v2 << '\n';
else {
std::cin >> v >> w;
int mx = 0, sum = 0;
if (bzin[v]) {
mx = qryma(1, 1, n, bzin[v], bzout[v]);
sum = qryv(1, 1, n, bzin[v], bzout[v]);
}
if (int f = fa[v]) {
if (bdin[f] < bfn[v]) {
mx = std::max(qryma(1, 1, n, bdin[f], bfn[v] - 1), mx);
reduce(sum += qryv(1, 1, n, bdin[f], bfn[v] - 1) - mod);
}
if (bfn[v] < bdout[f]) {
mx = std::max(qryma(1, 1, n, bfn[v] + 1, bdout[f]), mx);
reduce(sum += qryv(1, 1, n, bfn[v] + 1, bdout[f]) - mod);
}
}
mx = std::max(mx, val[fa[fa[v]]]);
v1 = std::max(v1, (LL) mx * (val[v] + w));
reduce(sum += val[fa[fa[v]]] - mod);
reduce(v2 += mul(sum, w << 1) - mod);
mdf(1, 1, n, bfn[v], val[v] += w);
assert(val[v] <= 2000000000);
}
}
std::cerr << "time used: " << clock() / (double) CLOCKS_PER_SEC << std::endl;
return 0;
}
后期#9
A
考虑子树问题独立,那么父亲转移要么和这个儿子一样,要么和这个儿子不一样。
两种分别对应的权值为 \(a_i\) 与 \(sum - a_i\),\(sum\)为子树和。
那么考虑如果无法使得\(x\)赋值满足条件,即所有儿子做一个背包之后的最小大于\(a_x\)。
考虑尽量避免这种情况,那么\(a_i\)是定值无法改变,我们尽量让 \(sum - a_i\) 小,即 \(sum\) 小。
考虑每次合并时,选取 \(\sum val \leq a_x\) 的最大方案,那么此时 \(sum\) 增加小。
点击查看代码
//晦暗的宇宙,我们找不到光,看不见尽头,但我们永远都不会被黑色打倒。——Quinn葵因
#include<bits/stdc++.h>
#define ll long long
#define N 1005
#define M 5005
int n;
using std::bitset;
bitset<M>F[N];//背包
int a[N];
using std::vector;
vector<int>T[N];
int sum[N];
int b[N];
bool flg = 1;
inline void dfs(int u){
F[u][0] = 1;
for(auto v : T[u]){
dfs(v);
if(flg)F[u] = (F[u] << (a[v])) | (F[u] << (sum[v] - a[v]));
sum[u] = sum[u] + sum[v];
}
int now = -1;
for(int i = a[u];i >= 0;--i)
if(F[u][i]){now = i;break;}
if(now == -1){flg = 0;return ;}
sum[u] = sum[u] + a[u] - now;
}
int main(){
// freopen("q.in","r",stdin);
// freopen("q.out","w",stdout);
scanf("%d",&n);
for(int i = 2;i <= n;++i){
int x;
scanf("%d",&x);
T[x].push_back(i);
}
for(int i = 1;i <= n;++i)
scanf("%d",&a[i]);
dfs(1);
puts(flg ? "YES" : "NO");
}
后期#10
A
考虑期望就要想到拆成期望性,那么一条边把树分成两块,一定是把黑白对跨越,然后对着推柿子就行了。
B
考虑环上答案是 \(s_{x,y}\) 相同,那么可以考虑对其进行 \(s_{x,y}\) 相同位置的完美匹配。
然后对 \(s_{x,y}\) 找到比他小的相邻的即可。
C
考虑预处理出\(f_{u,i}\)表示用\(u\)作为起点,然后考虑二分答案,转为或起来为全局即可使用FWT。
后期#11
A
笛卡尔树上启发式合并即可
B
二分答案,接下来相当于是从序列中选择至少 \(n-k\) 个位置,剩下的位置任意取值,使得相邻两项相差不超过二分的值 \(lim\)。
因为没有选取的位置可以任意取值,所以只需要相邻两个选择的位置的数相差不超过 \(lim\) 乘上它们之间的距离。这样可以写出一个 dp,即 \(dp_i\) 表示限制最后一个选择的位置为 \(i\) 时,最多可以选几个位置,那么转移为
当没有任何一个 \(j\) 满足括号内条件时,\(\max\) 取 0。这样我们有了一个 \(O(n^2\log A)\) 的做法。
考虑优化 dp,注意到题中限制可以拆成两个线性限制,可以使用 cdq分治 + 树状数组 等方法,在 \(O(n\log^2 n)\) 时间内计算这个 dp(可以参考“三维偏序”模板)。这样即可在 \(O(n\log^2 n\log A)\) 时间内计算出答案。
#12
A
ddp板子题。
B/C
暂无。
#13
A
考虑竞赛图中有按初出度排序后,有如下结论:
-每个连通块对应序列上一个一段连续区间
-一个位置\(x\)是一个连通块的对应的连续区间末尾当切仅当自己及前面所有点的出度之和等\(\binom{x}{2}\)
更换实际上是一个出度减一,一个出度加1,实际上是区间修改即可。
#15
C
签到。
考虑莫比乌斯反演后,使用线段树分治,然后使用并查集对缩点,考虑记录连通块内的路径数量。
点击查看代码
#include <bits/stdc++.h>
#define ll long long
using namespace std;
#define getchar() (_S == _T && (_T = (_S = _B) + fread(_B, 1, 1 << 20, stdin), _S == _T) ? EOF : *_S++)
char _B[1 << 20], *_S = _B, *_T = _B;
template <class T> inline void fr(register T &a, register char ch = getchar()) {
for (a = 0; ch < '0' || ch > '9'; ch = getchar()) ;
for (; ch >= '0' && ch <= '9'; ch = getchar()) a = a * 10 + (ch - '0');
}
template <class T, class... Y> inline void fr(register T &t, register Y &... a) { fr(t), fr(a...); }
const int N = 1e5, M = 1e6, Q = 1e2, P = 12999997, B = 392699;
int n, q, top, etot = 1, ptot, lst[N + 10], hd[P + 10], mu[M + 10], pr[M + 10], pnum[M + 10], lg2[M + 10], arr[15], c[1 << 8 | 10], fa[P + 5], sz[P + 5];
struct Element { int x, y, nxt; } e[P + 5];
struct Stack { int u, v, x; } stk[P + 5];
struct Edge { int u, v, w; } E[N + 10];
vector<Edge> vec[Q << 2 | 10];
bool isnp[M + 10]; ll ans[Q + 10];
void Init() {
isnp[1] = mu[1] = 1;
for (register int i = 2; i <= M; i++) {
if (isnp[i] == 0) pnum[++ptot] = i, mu[i] = -1, pr[i] = i;
for (register int j = 1; j <= ptot && i * pnum[j] <= M; j++) {
isnp[i * pnum[j]] = 1, pr[i * pnum[j]] = pnum[j];
if (i % pnum[j] == 0) break;
mu[i * pnum[j]] = -mu[i];
}
lg2[i] = lg2[i >> 1] + 1;
}
}
int insert(register int x, register int y) {
register int u = (1ll * B * x + y) % P;
for (register int i = hd[u]; i; i = e[i].nxt) if (e[i].x == x && e[i].y == y) return i;
return e[++etot] = {x, y, hd[u]}, hd[u] = etot;
}
void modify(register int u, register int ql, register int qr, register int v, register int l = 1, register int r = q + 1) {
if (ql > r || qr < l) return ;
if (ql <= l && qr >= r) return vec[u].push_back(E[v]), void();
register int mid = (l + r) >> 1;
modify(u << 1, ql, qr, v, l, mid), modify(u << 1 | 1, ql, qr, v, mid + 1, r);
}
int find(register int u) {
if (u == fa[u]) return u;
return find(fa[u]);
}
void merge(register int x, register int u, register int v) {
u = find(u), v = find(v);
if (u == v) return;
if (sz[u] > sz[v]) swap(u, v);
fa[u] = v, stk[++top] = {u, v, x}, ans[0] += 1ll * mu[x] * sz[u] * sz[v], sz[v] += sz[u];
}
inline void pop() {
register int u = stk[top].u, v = stk[top].v, x = stk[top].x;
top--, fa[u] = u, sz[v] -= sz[u], ans[0] -= 1ll * mu[x] * sz[u] * sz[v];
}
void stdc(register int u, register int l = 1, register int r = q + 1) {
register int lsttop = top;
for (auto i : vec[u]) {
register int u = i.u, v = i.v, w = i.w; arr[0] = 0;
while (w > 1) {
arr[++arr[0]] = pr[w];
while (w % arr[arr[0]] == 0) w /= arr[arr[0]];
}
for (register int j = 1, k; j < (1 << arr[0]); j++) k = j & -j, c[j] = c[j ^ k] * arr[lg2[k] + 1];
for (register int j = 0; j < (1 << arr[0]); j++) merge(c[j], insert(c[j], u), insert(c[j], v));
}
if (l == r) ans[l] = ans[0];
else {
register int mid = (l + r) >> 1;
stdc(u << 1, l, mid), stdc(u << 1 | 1, mid + 1, r);
}
while (top > lsttop) pop();
}
signed main() {
scanf("%d",&n);
Init();
for (register int i = 1; i < n; i++) scanf("%d%d%d",&E[i].u,&E[i].v,&E[i].w), lst[i] = 1;
scanf("%d",&q);
for (register int i = 1, k, w; i <= q; i++) scanf("%d%d",&k,&w), modify(1, lst[k], i, k), lst[k] = i + 1, E[k].w = w;
for (register int i = 1; i < n; i++) modify(1, lst[i], q + 1, i);
for (register int i = 1; i < min(P + 1, ((n + q) << 8)); i++) fa[i] = i, sz[i] = 1;
c[0] = 1, stdc(1);
for (register int i = 1; i <= q + 1; i++) printf("%lld\n", ans[i]);
return 0;
}
B
设 \(S_i\) 为 \(i\) 级分化树的 $\sum_{i=1}{num}\sum_{j=1}{i}dis(i, j) $,设 \(s_{i,j}\) 为 \(i\) 级仿生树中所有点到 \(j\) 关键点的边权和,我们发现 \(S_i = n \times S_{i - 1} + \sum_{j=1}^{n} n^{i-1} \times (n - 1) \times s_{i-1,j} + S_1 \times n^{2i-2}\) ,他们分别代表复制的 \(n\) 棵 \(i-1\) 级仿生树的权值,每个复制的仿生树到达该仿生树最重要关键点的权值的贡献,新连接的边带来的贡献。而 \(s_{i,j} = \sum_{j=1}^n s_{i-1,j} + s_{1,j} \times n^{i-1}\),他们分别代表每个复制的仿生树到达该仿生树最重要关键点的权值的贡献,新连接的边带来的贡献。我们设 \(P_i = \sum_{j=1}^n s_{i,j}\),于是 \(P_i = n\times P_{i-1} + 2S_1 \times n^{i-1}\),所以 \(S_i = n \times S_{i - 1} + n^{i-1} \times (n - 1) \times P_{i-1} + S_1 \times n^{2i-2}\),我们求出 \(S_1\) 为 \(1\) 的情况并再最后乘上真正的 \(S_1\) 便能 \(O(k)\) 求出 \(k\) 级分化树的答案(以下 \(S_1\) 均用 \(1\) 代替)。之后我们发现 \(P_i = n\times P_{i-1} + 2n^{i-1}\) 直接暴力展开就能推出 \(P_i = 2\times i\times n^{i-1}\)。所以 \(S_i = n \times S_{i - 1} + n^{i-1} \times (n - 1) \times 2\times (i-1)\times n^{i-2} + n^{2i-2}\)。我们再次暴力展开发现:
$S_i = \sum_{j=1}^{j=i} n^{i-j} \times (n^{j-1} \times (n - 1) \times 2\times (j-1)\times n^{j-2} + n^{2j-2}) $
$= \sum_{j=1}^{j=i} n^{i+j-3} \times (n - 1) \times 2\times (j-1) + n^{i+j-2} $
\(= \sum_{j=1}^{j=i} n^{i+j-2} \times(2j-1) - n^{i+j-3} \times (2j-2)\)
\(= \frac{n^{i-2}((2i-1)n^{i+1}-2in^i+n)}{n-1}\)
这样就可以 \(O(\log k)\) 得到答案。
同时可以先 \(O(\sqrt{MOD})\) 来预处理,然后单次查询就可以做到
#16
A
考虑每个叶子被离他最近的带权点支配。
\(S\)个\(\leq k\)的数至少有一个\(k\)的方案为\(k^S - (k- 1)^S\)
直接每次跳父亲暴力维护即可
B
考虑这是经典模型:
答案为 \([l,r] : a_l + \sum_{i = l + 1}^r \max(a_i - a_{i - 1},0)\)
考虑加等差数列实际上区间差分\(+v\),查询区间正数带权和,单点减。
一个正数差分的权值为 \((n - i + 1)(i - 1)\)
考虑首个元素的代价:一次加法为 \(\sum_{i = l}^r v(n - i + 1)(i - l + 1)\)
考虑加法时使用类似于吉老师线段树即可。