补题

1. 1303F

大意: 给定$n\times m$矩阵, 初始全$0$, 若相邻两格元素相同, 那么它们连通. 每次操作修改元素的值, 求矩阵中连通块的个数. 


因为颜色比较少, 可以分别考虑每种颜色, 那么修改操作就等价于删点和添点. 添点用并查集很容易维护, 删点倒序处理即可.  

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(_i,_a,_n) for(int _i=_a;_i<=_n;++_i)
#define PER(_i,_a,_n) for(int _i=_n;_i>=_a;--_i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(_a) ({REP(_i,1,n) cout<<_a[_i]<<',';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+7, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;}
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=0;char p=getchar();while(p<'0'||p>'9')p=getchar();while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();return x;}
//head


const int dx[]={0,0,-1,1};
const int dy[]={-1,1,0,0};
const int N = 2e6+10;
int n,m,q,a[350][350];
struct _ {
    int x,y,id;
};
vector<_> del[N],add[N];
int fa[N],ans[N];
int Find(int x) {return fa[x]?fa[x]=Find(fa[x]):x;}
int ID(int x, int y) {return (x-1)*m+y;}

void solve(vector<_> &v, int c) {
    REP(i,1,n) REP(j,1,m) a[i][j] = 0;
    REP(i,0,n*m) fa[i] = 0;
        for (auto t:v) {
        a[t.x][t.y] = 1;
        int w = 1;
        REP(i,0,3) {
            int x=t.x+dx[i],y=t.y+dy[i];
            if (1<=x&&x<=n&&1<=y&&y<=m&&a[x][y]) {
                int u=Find(ID(t.x,t.y)),v=Find(ID(x,y));
                if (u!=v) --w,fa[u]=v;
            }
        }
        ans[t.id] += w*c;
    }
}



int main() {
    scanf("%d%d%d",&n,&m,&q);
    int mx = 1;
    REP(i,1,q) {
        int x,y,c;
        scanf("%d%d%d",&x,&y,&c);
        if (a[x][y]==c) continue;
        mx = c;
        add[c].pb({x,y,i});
        del[a[x][y]].pb({x,y,i});
        a[x][y] = c;
    }
    REP(i,1,n) REP(j,1,m) del[a[i][j]].pb({i,j,0});
    REP(i,0,mx) reverse(del[i].begin(),del[i].end());
    REP(i,0,mx) if (add[i].size()) solve(add[i],1);
    REP(i,0,mx) if (del[i].size()) solve(del[i],-1);
    int now = 1;
    REP(i,1,q) {
        now += ans[i];
        printf("%d\n",now);
    }
}
View Code

2. 1301E

大意: 给定$n\times m$的格子, 每次操作求一个矩形内最大的合法商标面积. 


二分答案+二维RMQ即可.

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(_i,_a,_n) for(int _i=_a;_i<=_n;++_i)
#define PER(_i,_a,_n) for(int _i=_n;_i>=_a;--_i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(_a) ({REP(_i,1,n) cout<<_a[_i]<<',';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+7, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;}
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=0;char p=getchar();while(p<'0'||p>'9')p=getchar();while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();return x;}
//head
 
 
 
const int N = 555;
int n,m,q;
char s[N][N];
int R[N][N],G[N][N],Y[N][N],B[N][N];
int Log[N],f[11][11][N][N];
 
void init() {
    Log[0] = -1;
    REP(i,1,N-1) Log[i]=Log[i>>1]+1;
    REP(k,1,10) for (int i=1;i+(1<<k-1)<=n;++i) { 
        REP(j,1,m) { 
            f[k][0][i][j] = max(f[k-1][0][i][j],f[k-1][0][i+(1<<k-1)][j]);
        }
    }
    REP(l,1,10) REP(k,0,10) {
        for (int i=1;i+(k?(1<<k-1):0)<=n;++i) {
            for (int j=1;j+(1<<l-1)<=m;++j) {
                f[k][l][i][j] = max(f[k][l-1][i][j],f[k][l-1][i][j+(1<<l-1)]);
            }
        }
    }
}
int RMQ(int x1, int y1, int x2, int y2) {
    if (x2<x1||y2<y1||x1<=0||y1<=0||x2>n||y2>m) return 0;
    int u = Log[x2-x1+1], v = Log[y2-y1+1];
    x2 += -(1<<u)+1, y2 += -(1<<v)+1;
    auto A = f[u][v];
    return max({A[x1][y1],A[x1][y2],A[x2][y1],A[x2][y2]});
}
 
 
int main() {
    scanf("%d%d%d",&n,&m,&q);
    REP(i,1,n) scanf("%s",s[i]+1);
    REP(i,1,n) REP(j,1,m) if (s[i][j]=='R') { 
        R[i][j] = min({R[i-1][j],R[i][j-1],R[i-1][j-1]})+1;
    }
    REP(i,1,n) PER(j,1,m) if (s[i][j]=='G') {
        G[i][j] = min({G[i-1][j],G[i][j+1],G[i-1][j+1]})+1;
    }
    PER(i,1,n) REP(j,1,m) if (s[i][j]=='Y') {
        Y[i][j] = min({Y[i+1][j],Y[i][j-1],Y[i+1][j-1]})+1;
    }
    PER(i,1,n) PER(j,1,m) if (s[i][j]=='B') {
        B[i][j] = min({B[i+1][j],B[i][j+1],B[i+1][j+1]})+1;
    }
    REP(i,1,n) REP(j,1,m) if (s[i][j]=='R') {
        f[0][0][i][j] = min({R[i][j],G[i][j+1],Y[i+1][j],B[i+1][j+1]});
    }
    init();
    while (q--) {
        int x1,y1,x2,y2;
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        int l=1,r=m/2,ans=0;
        while (l<=r) {
            if (RMQ(x1+mid-1,y1+mid-1,x2-mid,y2-mid)>=mid) ans=mid,l=mid+1;
            else r=mid-1;
        }
        printf("%d\n",4*ans*ans);
    }
}
View Code

3. 1301F

大意: 给定$n\times m$的棋盘, 每一步可以走相邻的格子,或相同颜色的格子. $q$次询问,求两点之间最短路. 


假设只走相邻格, 那么答案就是两点间的曼哈顿距离. 否则至少走一次同色格, 并且每种同色格之间最多走一次.

对每种颜色建一个虚点, 向对应颜色的格子连一条$0$边, 显然最短路中每个虚点最多经过$1$次.

预处理出每个虚点到每个格子的最短路, 那么最终答案就是虚点到起点最短路+虚点到终点最短路+1

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(_i,_a,_n) for(int _i=_a;_i<=_n;++_i)
#define PER(_i,_a,_n) for(int _i=_n;_i>=_a;--_i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(_a) ({REP(_i,1,n) cout<<_a[_i]<<',';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+7, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;}
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=0;char p=getchar();while(p<'0'||p>'9')p=getchar();while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();return x;}
//head
 
 
const int dx[]={0,0,1,-1};
const int dy[]={1,-1,0,0};
const int N = 1e3+10, K = 50;
int n,m,k,a[N][N],d[K][N][N],vis[K];
vector<pii> g[K];
queue<pii> q;
 
void bfs(int x) {
    memset(d[x],0x3f,sizeof d[x]);
    memset(vis,0,sizeof vis);
    for (auto &t:g[x]) {
        d[x][t.x][t.y] = 0;
        q.push(t);
    }
    while (q.size()) {
        pii u = q.front(); q.pop();
        int c = a[u.x][u.y], w = d[x][u.x][u.y]+1;
        if (!vis[c]) {
            vis[c] = 1;
            for (auto &v:g[c]) {
                if (d[x][v.x][v.y]>w) {
                    d[x][v.x][v.y]=w;
                    q.push(v);
                }
            }
        }
        REP(i,0,3) {
            pii v(u.x+dx[i],u.y+dy[i]);
            if (1<=v.x&&v.x<=n&&1<=v.y&&v.y<=m) {
                if (d[x][v.x][v.y]>w) {
                    d[x][v.x][v.y]=w;
                    q.push(v);
                }
            }
        }
    }
}
 
int main() {
    scanf("%d%d%d",&n,&m,&k);
    REP(i,1,n) REP(j,1,m) {
        scanf("%d",a[i]+j);
        g[a[i][j]].pb(pii(i,j));
    }
    REP(i,1,k) bfs(i);
    int q;
    scanf("%d",&q);
    while (q--) {
        int x1,y1,x2,y2;
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        int ans = abs(x2-x1)+abs(y2-y1);
        REP(i,1,k) ans = min(ans, d[i][x1][y1]+d[i][x2][y2]+1);
        printf("%d\n",ans);
    }
}
View Code

4. 1244F

大意: 给定一个环, 对于每个时刻, 考虑点$i$和它左右两侧的点, 若黑色个数多, 则点$i$变为黑色, 否则变为白色. 求$k$时间后每个点颜色. 


首先可以观察到假设相邻两点同色, 那么这两点颜色永远不会改变. 所以就把这个环拆成若干个段, 每段之间是独立的, 分别模拟一下即可.

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(_i,_a,_n) for(int _i=_a;_i<=_n;++_i)
#define PER(_i,_a,_n) for(int _i=_n;_i>=_a;--_i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(_a) ({REP(_i,1,n) cout<<_a[_i]<<',';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+7, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;}
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=0;char p=getchar();while(p<'0'||p>'9')p=getchar();while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();return x;}
//head



const int N = 1e6+10;
int n,k,vis[N],v[N];
char s[N],p[N];

void work(int l, int r, int k) {
    char A = s[(l-1+n)%n], B = s[(r+1)%n];
    while (v[l]&&k) {
        s[l] = A, s[r] = B;
        v[l] = v[r] = 0;
        --k,(++l)%=n,r=(r-1+n)%n;
    }
    while (v[l]) v[l]=1,s[l]="BW"[s[(l-1+n)%n]=='B'],(++l)%=n;
}

int main() {
    scanf("%d%d%s",&n,&k,s);
    int pos = -1;
    REP(i,0,n-1) if (s[i]==s[(i-1+n)%n]) pos = i;
    if (pos<0) {
        if (k&1) REP(i,0,n-1) s[i]="BW"[s[i]=='B'];
        puts(s);
        return 0;
    }
    REP(i,0,n-1) {
        //标记每段的开头: BBWB
        if (s[(i-1+n)%n]==s[(i-2+n)%n]&&s[(i-1+n)%n]!=s[i]&&s[i]!=s[(i+1)%n]) vis[i] = 1;
        //标记每段的结尾: WBB
        if (s[(i+1)%n]==s[(i+2)%n]&&s[(i+1)%n]!=s[i]) vis[i] += 2;
    }
    REP(i,0,n-1) if (vis[i]&1) {
        int j = i;
        while (vis[j]<=1) v[j]=1,(++j)%=n;
        v[j]=1;
        work(i,j,k);
    }
    puts(s);
}
View Code

5. 1288D

大意: 给定$n$个数组, 要求选出$i,j$, 构造数组$b_k=max(a_{i,k},a_{j,k})$, 满足$b_k$最小值最大. 


状压求出状态为$s$的最小值的最大值$f[s]$, 那么答案就是$max(f[s],f[mx\oplus s])$

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(_i,_a,_n) for(int _i=_a;_i<=_n;++_i)
#define PER(_i,_a,_n) for(int _i=_n;_i>=_a;--_i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(_a) ({REP(_i,1,n) cout<<_a[_i]<<',';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+7, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;}
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=0;char p=getchar();while(p<'0'||p>'9')p=getchar();while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();return x;}
//head



const int N = 1e6+10;
int n,m,a[N];
pii f[N];
void dfs(int d, int s, int x, int id) {
    if (d==m) return f[s]=max(f[s],pii(x,id)),void();
    dfs(d+1,s,x,id),dfs(d+1,s^1<<d,min(x,a[d]),id);
}

int main() {
    scanf("%d%d",&n,&m);
    int mx = (1<<m)-1;
    REP(i,1,n) {
        REP(j,0,m-1) scanf("%d",a+j);
        dfs(0,0,2e9,i);
    }
    int ans = -1, id = 0;    
    REP(i,0,mx) {
        int w = min(f[i].x,f[mx^i].x);
        if (w>ans) ans = w, id = i;
    }
    printf("%d %d\n",f[id].y,f[mx^id].y);
}
View Code

6. 1285F

大意: 给定序列$a$, 求$\max\limits_{1\le i<j\le n}LCM(a_i,a_j)$ 


考虑枚举gcd, 问题就转化为求两个互质的数的乘积的最大值.

考虑从大到小遍历, 维护一个栈. 假设当前遍历到$x$, 每次从栈中取出最小元素$y$.

假设$x$和$y$互质的话, 那么$y$就可以从栈中取出, 这是因为以后遍历到的数会更小, 一定不会更优.直到栈中没有与$x$互质的为止.

求一个序列中是否有与$x$互质的数, 就相当于求

$\sum\limits_{i=1}^{mx}{cnt}_i[gcd(i,x)=1]=\sum\limits_{d|x}\mu(d)\sum\limits_{i=1}^{mx} [d|x]{cnt}_i$

动态维护$f(d) = \sum\limits_{i=1}^{mx} [d|x]{cnt}_i$即可.

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(_i,_a,_n) for(int _i=_a;_i<=_n;++_i)
#define PER(_i,_a,_n) for(int _i=_n;_i>=_a;--_i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(_a) ({REP(_i,1,n) cout<<_a[_i]<<',';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+7, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;}
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=0;char p=getchar();while(p<'0'||p>'9')p=getchar();while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();return x;}
//head



const int N = 1e5+10;
int n,a[N],mu[N],f[N];
vector<int> c[N],s;

int main() {
    REP(i,1,N-1) for (int j=i; j<N; j+=i) c[j].pb(i);
    mu[1] = 1;
    REP(i,1,N-1) for (int j=2*i; j<N; j+=i) mu[j]-=mu[i];
    scanf("%d", &n);
    ll ans = 0;
    REP(i,1,n) {
        int t;
        scanf("%d", &t);
        a[t] = 1;
        ans = max(ans, (ll)t);
    }
    REP(g,1,N-1) { 
        PER(x,1,(N-1)/g) if (a[g*x]) {
            int tot = 0;
            for (int t:c[x]) tot += mu[t]*f[t];
            while (tot) {
                int y = s.back();
                if (gcd(x,y)==1) {
                    ans = max(ans, (ll)g*x*y);
                    --tot;
                }
                for (int t:c[y]) --f[t];
                s.pop_back();
            }
            for (int t:c[x]) ++f[t];
            s.push_back(x);
        }
        while (s.size()) { 
            for (int t:c[s.back()]) --f[t];
            s.pop_back();
        }
    }
    printf("%lld\n", ans);
}
View Code

7.  1313D

大意: 给定$n$个区间, 保证每个点最多被$k$个区间覆盖, 可以任意删除掉区间, 求最多多少个点被奇数个区间覆盖. 


考虑离散化, 把连续一段相同状态的点合并一下, 然后状压$DP$即可.

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <set>
#include <map>
#define REP(_i,_a,_n) for(int _i=_a;_i<=_n;++_i)
#define x first
#define y second
#define pb push_back
using namespace std;
typedef pair<int,int> pii;
 
int n,m,k;
vector<pii> events;
set<int> s;
int dp[2][260];
 
int main() {
    scanf("%d%d%d",&n,&m,&k);
    REP(i,1,n) {
        int l,r;
        scanf("%d%d",&l,&r);
        events.pb(pii(l,i));
        events.pb(pii(r+1,-i));
    }
    sort(events.begin(),events.end());
    s.insert(events[0].y);
    map<int,int> ID[2];
    int cur = 0;
    auto upd = [&]() {
        cur ^= 1;
        memset(dp[cur],0xef,sizeof dp[0]);
        ID[cur].clear();
        int p = 0, mx = (1<<s.size())-1;
        for (auto &t:s) ID[cur][t] = p++;
    };
    auto get = [&](int x) {
        int sta = 0, p = 0;
        for (auto &t:ID[cur]) if (x>>(p++)&1) {
            if (ID[cur^1].count(t.x)) sta ^= 1<<ID[cur^1][t.x];
        }
        return sta;
    };
    for (int i=1; i<events.size(); ++i) {
        int len = events[i].x-events[i-1].x;
        if (len&&s.size()) {
            upd();
            int mx = (1<<s.size())-1;
            if (ID[cur^1].empty()) {
                int ma = *max_element(dp[cur^1],dp[cur^1]+256);
                REP(j,0,mx) dp[cur][j] = ma+__builtin_parity(j)*len;
            }
            else {
                REP(j,0,mx) dp[cur][j] = max(dp[cur][j], dp[cur^1][get(j)]+__builtin_parity(j)*len);
            }
        }
        int id = events[i].y;
        if (id<0) {
            id = -id, s.erase(id), upd();
            int mx = (1<<s.size())-1;
            REP(j,0,mx) {
                int sta = get(j);
                dp[cur][j] = max(dp[cur^1][sta],dp[cur^1][sta^(1<<ID[cur^1][id])]);
            }
        }
        else s.insert(id);
    }
    int ans = 0;
    REP(i,0,255) ans = max(ans, dp[cur][i]);
    printf("%d\n", ans);
}
View Code

8. 1325F

大意: 给定$n$节点无向连通图, 求找到一个$\lceil \sqrt{n} \rceil$个点的独立集, 或长度不少于$\lceil \sqrt{n} \rceil$的简单环. 


考虑$dfs$树, 对深度按$mod \lceil \sqrt{n} \rceil-1$分类, 那么如果找不到环的话, 每个分类的点之间一定不含边, 可以构成独立集, 并且最大的分类点数一定是不少于$\lceil \sqrt{n} \rceil$

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(_i,_a,_n) for(int _i=_a;_i<=_n;++_i)
#define PER(_i,_a,_n) for(int _i=_n;_i>=_a;--_i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(_a) ({REP(_i,1,n) cout<<_a[_i]<<',';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+7, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;}
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=0;char p=getchar();while(p<'0'||p>'9')p=getchar();while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();return x;}
//head



const int N = 1e6+50;
int n,m,k,dep[N],fa[N],vis[N];
vector<int> g[N],h[N];

void get(int x, int y) {
    vector<int> v;
    do v.pb(x),x=fa[x]; while (x!=y);
    v.pb(y);
    printf("2\n%d\n",(int)v.size());
    for (int t:v) printf("%d ",t);
    hr,exit(0);
}

void dfs(int x, int f, int d) {
    vis[x]=1,dep[x]=d,fa[x]=f;
    h[d%(k-1)].pb(x);
    for (int y:g[x]) if (y!=f) {
        if (!vis[y]) dfs(y,x,d+1);
        else if (d-dep[y]>=k-1) get(x,y);
    }
}

int main() {
    scanf("%d%d",&n,&m);
    k = ceil(sqrt(n));
    REP(i,1,m) {
        int u,v;
        scanf("%d%d",&u,&v);
        g[u].pb(v),g[v].pb(u);
    }
    dfs(1,0,0);
    REP(i,0,k-2) if (h[i].size()>=k) {
        puts("1");
        REP(j,0,k-1) printf("%d ", h[i][j]);
        return hr,0;
    }
}
View Code

9. 1325E

大意: 给定序列, 每个元素最多$7$个因子, 求一个最短的积为完全平方数的子序列.


显然每个数最多只有两个素因子, 考虑对每个素数建一个点, 如果一个元素有两个素因子, 那么就再它们之间连一条边, 否则再建一个虚点, 把这个素数向虚点连边. 那么显然这个图的最小环就是答案.

可以注意到对于$> \sqrt{max A_i}$的点之间是不含边的, 所以一个环一定至少包含一个$<\sqrt{maxA_i}$的点, 所以可以枚举这个点, 用$bfs$求最小环, 复杂度就为$O(168n)$

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(_i,_a,_n) for(int _i=_a;_i<=_n;++_i)
#define PER(_i,_a,_n) for(int _i=_n;_i>=_a;--_i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(_a) ({REP(_i,1,n) cout<<_a[_i]<<',';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+7, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;}
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=0;char p=getchar();while(p<'0'||p>'9')p=getchar();while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();return x;}
//head



const int N = 1e6+50;
int n,a[N],cnt,v[N],primes[N],d1[N],d2[N],f1[N],f2[N];
vector<int> g[N],d[N];
struct _ {int x,d,f;};
queue<_> q;

int main() {
    REP(i,2,1000) if (!v[i]) {
        primes[++cnt] = i;
        for (int j=i*i; j<=1000; j+=i) v[j]=1;
    }
    scanf("%d", &n);
    REP(i,1,n) scanf("%d",a+i);
    REP(i,1,n) {
        REP(j,1,cnt) if (a[i]%primes[j]==0) {
            int x = 0;
            while (a[i]%primes[j]==0) ++x,a[i]/=primes[j];
            if (x&1) d[i].pb(j);
        }
        if (a[i]>1) d[i].pb(a[i]);
        if (d[i].empty()) return puts("1"),0;
        if (d[i].size()==1) g[0].pb(d[i][0]),g[d[i][0]].pb(0);
        else g[d[i][0]].pb(d[i][1]),g[d[i][1]].pb(d[i][0]);
    }
    int ans = INF;
    REP(i,0,cnt) {
        sort(g[i].begin(),g[i].end());
        if (unique(g[i].begin(),g[i].end())!=g[i].end()) return puts("2");
        d1[0] = d2[0] = INF, f1[0] = f2[0] = -1;
        REP(j,1,n) for (int t:d[j]) d1[t]=d2[t]=INF,f1[t]=f2[t]=-1;
        for (int t:g[i]) q.push({t,1,t});
        while (q.size()) {
            auto [u,d,f] = q.front(); q.pop();
            if (d1[u]==INF) d1[u]=d,f1[u]=f;
            else if (d2[u]==INF&&f1[u]!=f) d2[u]=d,f2[u]=f;
            else continue;
            for (int v:g[u]) if (v!=i) q.push({v,d+1,f});
        }
        for (int v:g[i]) ans = min(ans, d2[v]+1);
    }
    if (ans>n) ans = -1;
    printf("%d\n", ans);
}
View Code

10. 1326E

大意: 给定排列$p,q$, $p_i$表示每个元素的值, $q_i$表示位置$q_i$有炸弹. 维护一个集合$A$, 从左到右依次把$p_i$添加到$A$中, 如果$i$位置有炸弹就将$A$中最大元素弹出. 对于$1\le i\le n$, 输出只考虑$q_1,q_2,...,q_{i-1}$位置有炸弹时最后$A$中的最大元素. 


跟756C非常像. 假设答案为$x$, 那么只用考虑$p$中$\ge x$的元素, 每次添加一个$\ge x$的元素就等价于一次$push$操作, 每碰到一个炸弹就等价于一次$pop$操作, 假设最终栈中仍有元素, 那么就说明答案是$\ge x$的. 所以问题就转化为给定一个$push$和$pop$的操作序列, 求判断最终栈是否非空. 把$push$看做$1$, $pop$看做$-1$, 栈非空就等价于存在一个后缀和$>0$, 用线段树维护一个最大后缀和即可.

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(_i,_a,_n) for(int _i=_a;_i<=_n;++_i)
#define PER(_i,_a,_n) for(int _i=_n;_i>=_a;--_i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(_a) ({REP(_i,1,n) cout<<_a[_i]<<',';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+7, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;}
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=0;char p=getchar();while(p<'0'||p>'9')p=getchar();while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();return x;}
//head
 
 
 
const int N = 1e6+10; 
int n,a[N],b[N],pos[N];
struct _ {
    int ma,sum;
} tr[N<<2];
void pu(int o) {
    tr[o].sum=tr[lc].sum+tr[rc].sum;
    tr[o].ma=max(tr[rc].ma,tr[rc].sum+tr[lc].ma);
}
void add(int o, int l, int r, int x, int v) {
    if (l==r) { 
        tr[o].sum += v;
        tr[o].ma = max(0,tr[o].sum);
        return;
    }
    mid>=x?add(ls,x,v):add(rs,x,v);
    pu(o);
}
 
int main() {
    scanf("%d",&n);
    REP(i,1,n) scanf("%d",a+i),pos[a[i]]=i;
    REP(i,1,n) scanf("%d",b+i);
    add(1,1,n,pos[n],1);
    int ans = n;
    printf("%d ",ans);
    REP(i,1,n-1) {
        add(1,1,n,b[i],-1);
        while (!tr[1].ma) add(1,1,n,pos[--ans],1);
        printf("%d ", ans);
    }
    puts("");
}
View Code

11. 1326F1

大意: $n$个人, 给定$n\times n$矩阵表示每个人的认识关系. 一个排列$p$对应一个长$n-1$的二进制数, 假设$p_i$与$p_{i+1}$认识, 那么第$i$位为$1$, 否则为$0$. 对于所有长$n-1$的二进制数, 输出它对应多少种排列. 


设$f_{i,j,k}$表示只考虑集合$i$, 排列长度为$j$, 排列中最后一个数为$k$, 得到的二进制数为$x$的方案数.

考虑状态转移, 每次枚举一个不在$i$中的数$t$添加到排列末尾.

如果$t$和$k$不认识, 那么$f_{i\oplus 2^{x},j+1,t,x} \leftarrow f_{i,j,k,x}$

如果$t$和$k$认识, 那么$f_{i\oplus 2^{x},j+1,t,x\oplus 2^{j}} \leftarrow f_{i,j,k,x}$

这样的话复杂度是$O(n^3 4^n)$.

实际上可以注意到$j$就是$i$中$1$的个数, 这样就可以优化掉一个$n$, 并且有效的状态$x$的个数是$2^j$, 如果只遍历有效状态的话, 复杂度就可以达到$O(n^2 3^n)$

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(_i,_a,_n) for(int _i=_a;_i<=_n;++_i)
#define PER(_i,_a,_n) for(int _i=_n;_i>=_a;--_i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(_a) ({REP(_i,1,n) cout<<_a[_i]<<',';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+7, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;}
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=0;char p=getchar();while(p<'0'||p>'9')p=getchar();while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();return x;}
//head



int n;
vector<ll> f[1<<14][14];
char s[14][14];
ll ans[1<<14];

int main() {
    scanf("%d", &n);
    REP(i,0,n-1) scanf("%s", s[i]);
    int mx = (1<<n)-1;
    REP(i,0,mx) REP(j,0,n-1) if (i>>j&1) f[i][j].resize(1<<__builtin_popcount(i)-1);
    REP(i,0,n-1) f[1<<i][i][0] = 1;
    REP(i,1,mx) REP(j,0,n-1) if (i>>j&1) {
        int m = f[i][j].size()-1;
        REP(k,0,m) { 
            REP(x,0,n-1) if (i>>x&1^1) {
                int nxt = k;
                if (s[j][x]=='1') nxt ^= m+1;
                f[i^(1<<x)][x][nxt] += f[i][j][k];
            }
            if (i==mx) ans[k] += f[i][j][k];
        }
    }
    mx = (1<<n-1)-1;
    REP(i,0,mx) printf("%lld ",ans[i]);hr;
}
View Code

12. 1326F2

大意: 1326F1的加强版


$n$个人可以看成是一个无向图, 那么一个二进制数就对应图中的若干条链.

例如$111000111011$对应链的长度集合为$\{ 4,1,1,4,3\}$.

那么可以发现只要对应的链的长度集合相同, 答案就相同.

这个集合就相当于是$n$的整数拆分, $n=18$时, 拆分方案有$385$种.

那么只要暴搜出所有拆分, 求出每个拆分的答案即可.

对于一个拆分, 必须要保证相邻两条链之间是没有边的, 这个比较难处理.

比方说要求$\{1,1,2\}$的答案, 会把$\{1,3\},\{2,2\},\{4\}$的答案也算上.

可以注意到$\{1,1,2\},\{1,3\},\{2,2\},\{4\}$对应的二进制数是$001,011,101,111$.

也就是说$\{1,1,2\}$刚好是其他多算的二进制数的子集.

所以先求出不考虑相邻链是否有边的答案, 最后再做一次$IFWT_{and}$即可.

先用$dp$求出$f_{len,S}$表示长$len$,状态为$S$的链的条数.

那么可以得到拆分$T$的答案就为$\sum\prod f_{T_i,m_i}$

$m_i$是第$i$条链的状态, 并且$m_1,...,m_{|T|}$的二进制或和等于$2^{n}-1$. (因为$T_i$的和一定为$n$,就不用考虑$m_i$交叉的情况)

这个式子显然是$f_{T_i}$的集合并卷积的第$2^{n}-1$项, 可以用$FWT_{or}$求出.

总复杂度为$O((385 n+n^2)2^n)$

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(_i,_a,_n) for(int _i=_a;_i<=_n;++_i)
#define PER(_i,_a,_n) for(int _i=_n;_i>=_a;--_i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(_a) ({REP(_i,1,n) cout<<_a[_i]<<',';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 1e9+7, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;}
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=0;char p=getchar();while(p<'0'||p>'9')p=getchar();while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();return x;}
//head
 
 
const int N = 18;
int n, cnt;
ll dp[1<<N][N],f[N+1][1<<N],h[1<<N],d[400],ans[1<<N];
char a[N][N];
map<vector<int>,int> ID;
vector<int> g[400],v;
 
void dfs(int d, int s) {
    if (s>n) return;
    if (s==n) {
        ID[v] = ++cnt;
        g[cnt] = v;
        return;
    }
    REP(i,d,n) v.pb(i),dfs(i,s+i),v.pop_back();
}
 
void FWT_or(ll *a, int n, int tp) {
    int mx = (1<<n)-1;
    REP(i,0,n-1) REP(j,0,mx) {
        if (j>>i&1) a[j]+=tp*a[j^1<<i];
    }
}
void FWT_and(ll *a, int n, int tp) {
    int mx = (1<<n)-1;
    REP(i,0,n-1) REP(j,0,mx) {
        if (j>>i&1) a[j^1<<i]+=tp*a[j];
    }
}
 
int main() {
    scanf("%d", &n);
    REP(i,0,n-1) scanf("%s",a[i]);
    dfs(1,0);
    REP(i,0,n-1) dp[1<<i][i] = 1;
    int mx = (1<<n)-1;
    REP(i,0,mx) {
        REP(j,0,n-1) if (i>>j&1) REP(k,0,n-1) if (i>>k&1^1) {
            if (a[j][k]=='1') dp[i^1<<k][k] += dp[i][j];
        }
        int len = __builtin_popcount(i);
        REP(j,0,n-1) f[len][i] += dp[i][j];
    }
    REP(i,1,n) FWT_or(f[i],n,1);
    REP(i,1,cnt) {
        REP(j,0,mx) h[j] = 1;
        for (int j:g[i]) REP(k,0,mx) h[k]*=f[j][k];
        FWT_or(h,n,-1);
        d[i] = h[mx];
    }
    mx = (1<<n-1)-1;
    REP(i,0,mx) {
        v.clear();
        REP(j,0,n-1) {
            int k = j;
            while (k<n-1&&(i>>k&1)) ++k;
            v.pb(k-j+1);
            j = k;
        }
        sort(v.begin(),v.end());
        ans[i] = d[ID[v]];
    }
    FWT_and(ans,n-1,-1);
    REP(i,0,mx) printf("%lld ", ans[i]);hr;
}
View Code

13. 1327F

大意: 长$n$的序列, 每个元素范围$[0,2^{k}-1]$, 给定$m$个限制$(l_i,r_i,x_i)$, 表示$a_l,...a_r$的二进制与和等于$x$, 求有多少种方案. 


单独考虑二进制每位的答案, 假设$x_i$当前位为$0$, 那么就要求$[l_i,r_i]$中至少有$1$个$0$, 假设$x_i$当前位为$1$, 那么就要求$[l_i,r_i]$必须全为$1$. 那么枚举$1$和$0$的上次出现位置, 很容易写出$O(kn^2)$的$dp$.

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(_i,_a,_n) for(int _i=_a;_i<=_n;++_i)
#define PER(_i,_a,_n) for(int _i=_n;_i>=_a;--_i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(_a) ({REP(_i,1,n) cout<<_a[_i]<<',';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 998244353, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;}
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=0;char p=getchar();while(p<'0'||p>'9')p=getchar();while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();return x;}
//head


const int N = 1e5+10;
int n,k,m,l[N],r[N],x[N];
vector<pii> f[N];
int dp[2][2][N];
void add(int &a, int b) {a+=b;if (a>=P)a-=P;}

int solve(int d) {
    REP(i,1,n) f[i].clear();
    REP(i,1,m) f[r[i]].pb(pii(l[i],x[i]>>d&1));
    int cur = 0;
    memset(dp[cur],0,sizeof dp[0]);
    dp[0][0][0] = 1;
    //dp[i][z][j]
    //z = 0, 表示i填0, 上一个1的位置为j的方案数
    //z = 1, 表示i填1, 上一个0的位置为j的方案数
    REP(i,1,n) {
        cur ^= 1;
        memset(dp[cur],0,sizeof dp[0]);
        REP(j,0,i-1) REP(z,0,1) { 
            int &r = dp[!cur][z][j];
            add(dp[cur][z][j],r);
            add(dp[cur][!z][i-1],r);
        }
        for (auto &e:f[i]) {
            if (e.y==0) {
                REP(j,0,e.x-1) dp[cur][1][j] = 0;
            }
            else {
                REP(j,0,i-1) dp[cur][0][j] = 0;
                REP(j,e.x,i-1) dp[cur][1][j] = 0;
            }
        }
    }
    int ans = 0;
    REP(i,0,n-1) REP(z,0,1) add(ans,dp[cur][z][i]);
    return ans;
}

int main() {
    scanf("%d%d%d",&n,&k,&m);
    REP(i,1,m) scanf("%d%d%d",l+i,r+i,x+i);
    int ans = 1;
    REP(i,0,k-1) ans = (ll)ans*solve(i)%P;
    printf("%d\n", ans);
}
O(kn^2)代码

然后考虑$O(kn)$的做法, 假设$f_i$表示第$i$个数取$0$的方案数, 如果从$f_j$转移到$f_i$的话, 也就是说$[j+1,i-1]$全填$1$, 那么合法的$j$必须大于等于所有满足$r\le i-1$的$0$区间的$l$, 所以维护$0$区间的左端点最大值$ma$, 那么可转移的$j$的范围就是$[ma,i-1]$, 前缀优化一下即可. 对于$1$区间, 只要限制它覆盖的每个点$f_i$都为$0$即可. 这样复杂度就为$O(kn)$

#include <iostream>
#include <sstream>
#include <algorithm>
#include <cstdio>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <cstring>
#include <bitset>
#include <functional>
#include <random>
#define REP(_i,_a,_n) for(int _i=_a;_i<=_n;++_i)
#define PER(_i,_a,_n) for(int _i=_n;_i>=_a;--_i)
#define hr putchar(10)
#define pb push_back
#define lc (o<<1)
#define rc (lc|1)
#define mid ((l+r)>>1)
#define ls lc,l,mid
#define rs rc,mid+1,r
#define x first
#define y second
#define io std::ios::sync_with_stdio(false)
#define endl '\n'
#define DB(_a) ({REP(_i,1,n) cout<<_a[_i]<<',';hr;})
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int P = 998244353, INF = 0x3f3f3f3f;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll qpow(ll a,ll n) {ll r=1%P;for (a%=P;n;a=a*a%P,n>>=1)if(n&1)r=r*a%P;return r;}
ll inv(ll x){return x<=1?1:inv(P%x)*(P-P/x)%P;}
inline int rd() {int x=0;char p=getchar();while(p<'0'||p>'9')p=getchar();while(p>='0'&&p<='9')x=x*10+p-'0',p=getchar();return x;}
//head


const int N = 1e6+10;
int n,k,m,l[N],r[N],x[N],c[N],ma[N],f[N];
void add(int &a, int b) {a=(a+b)%P;}

int solve(int d) {
    REP(i,0,n+1) c[i] = ma[i] = f[i] = 0;
    REP(i,1,m) { 
        if (x[i]>>d&1) ++c[l[i]], --c[r[i]+1];
        else ma[r[i]] = max(ma[r[i]], l[i]);
    }
    REP(i,1,n) ma[i] = max(ma[i], ma[i-1]);
    f[0] = 1;
    int sum = 0;
    REP(i,1,n+1) {
        sum += c[i];
        if (!sum) f[i] = (f[i-1]-(ma[i-1]?f[ma[i-1]-1]:0))%P;
        add(f[i],f[i-1]);
    }
    return f[n+1]-f[n];
}

int main() {
    scanf("%d%d%d",&n,&k,&m);
    REP(i,1,m) scanf("%d%d%d",l+i,r+i,x+i);
    int ans = 1;
    REP(i,0,k-1) ans = (ll)ans*solve(i)%P;
    if (ans<0) ans += P;
    printf("%d\n", ans);
}
O(kn)代码

14. 1316F

 

posted @ 2020-02-14 22:47  uid001  阅读(210)  评论(0编辑  收藏  举报