1. 程式人生 > >[冬令營模擬]wzj的題目#1

[冬令營模擬]wzj的題目#1

T1 少膜一個,T3 暴力寫掛

強勢 rank1 -> rank2

一場比賽兩道線段樹分治,給力

 

T1 password

給你 m 個禁止字串,求長度為 n 的所有字串中至少包含這些禁止字串各一次的字串數量

$n \leq 10^9,m \leq 4,\sum len \leq 50$

 

sol:容斥一下就變成了“m 個禁止字串,一個都不出現的字串數量”

這個可以轉化成從 init 節點走 x 步走不到任意一個節點的方案數

矩陣加速即可

需要注意的是狀態壓縮被卡了,這種狀壓 + 矩乘可以用容斥來把矩陣變小

比賽的時候因為少膜了一下,掛了一個點

#include<bits/stdc++.h>
#define int long long
#define LL long long
using namespace std;
inline int read()
{
    int x = 0,f = 1;char ch = getchar();
    for(;!isdigit(ch);ch = getchar())if(ch == '-')f = -f;
    for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0';
    return
x * f; } const int N = 150; const int A = 10; const int MOD = 998244353; typedef vector<int> vec; typedef vector<vec> mat; mat mul(mat &A, mat &B) { mat C(A.size(), vec(B[0].size())); for (int i = 0; i < A.size(); ++i) { for (int k = 0; k < B.size(); ++k) {
for (int j = 0; j < B[0].size(); ++j) { C[i][j] = (C[i][j] + (LL)A[i][k] * B[k][j]) % MOD; } } } return C; } mat pow(mat A, int n) { mat B(A.size(), vec(A.size())); for (int i = 0; i < A.size(); ++i) B[i][i] = 1; while (n > 0) { if (n & 1) B = mul(B, A); A = mul(A, A); n >>= 1; } return B; } struct ACAutomata { int next[N][A], fail[N], end[N]; int root, L; int idx(char ch) { return ch - '0'; } int newNode() { for (int i = 0; i < A; ++i) next[L][i] = -1; end[L] = 0; return L++; } void init() { L = 0; root = newNode(); } void insert(char buf[]) { int len = strlen(buf); int now = root; for (int i = 0; i < len; ++i) { int ch = idx(buf[i]); if (next[now][ch] == -1) next[now][ch] = newNode(); now = next[now][ch]; } end[now]++; } void build() { queue<int> Q; fail[root] = root; for (int i = 0; i < A; ++i) { if (next[root][i] == -1) { next[root][i] = root; } else { fail[ next[root][i] ] = root; Q.push( next[root][i] ); } } while (!Q.empty()) { int now = Q.front(); Q.pop(); if (end[ fail[now] ]) end[now]++; for (int i = 0; i < A; ++i) { if (next[now][i] == -1) { next[now][i] = next[ fail[now] ][i]; } else { fail[ next[now][i] ] = next[ fail[now] ][i]; Q.push(next[now][i]); } } } } int query(int n) { mat F(L, vec(L)); for (int i = 0; i < L; ++i) { for (int j = 0; j < L; ++j) { F[i][j] = 0; } } for (int i = 0; i < L; ++i) { for (int j = 0; j < A; ++j) { int nt = next[i][j]; if (!end[nt]) F[i][nt]++; } } F = pow(F, n); int res = 0; for (int i = 0; i < L; ++i) { res = (res + F[0][i]) % MOD; } return res; } } ac; int m,n; char buf[20][60]; int skr(int x,int t) { int res = 1; while(t) { if(t & 1)res = res * x % MOD; x = x * x % MOD; t = t >> 1; }return res; } signed main() { freopen("password.in","r",stdin); freopen("password.out","w",stdout); m = read(),n = read(); int qwq = 0,qnq = skr(10,n); for(int i=1;i<=m;i++)cin>>buf[i]; for(int SS=1;SS<=(1<<m)-1;SS++) { int flg = __builtin_popcount(SS),S = (flg & 1) ? 1 : -1; ac.init(); memset(ac.next,-1,sizeof(ac.next)); memset(ac.fail,0,sizeof(ac.fail)); memset(ac.end,0,sizeof(ac.end)); for(int i=0;i<m;i++) if(SS & (1 << i))ac.insert(buf[i+1]); ac.build(); qwq += (S * ac.query(n)); } cout<<((((qnq-qwq) % MOD) + MOD) % MOD)<<endl; return 0; }
T1

 

T2 paint

APIO2014 特別行動隊,但是所有東西都可以為負

額...就是你現在有 n 個人,每個人有權值,你可以把它們分成若干組,第 i 人只能跟 $[i-l,i+r]$ 這些人一組,一組的權值和為 $s$,一組的權值為 $ax^2+bx+c$ ,讓你最大化權值

 

sol:因為一個人只能由 $[i-l,i+r]$ 轉移過來,我們可以考慮這個人有貢獻的區間,那顯然是 $[i+l,min(i+r,n)]$ ,我們可以線上段樹這個區間上打上這個人的 tag

然後我們按線段樹的 dfs 序掃這個區間,因為這個人有貢獻的區間一定在這個人右邊,所以處理到這個區間的時候,所有打在上面的 tag 已經處理完畢了

於是我們可以把從這個葉子到根的 logn 層凸包並起來二分

然後就寫完了。。

#include<bits/stdc++.h>
#define LL long long
#define DB long double
using namespace std;
inline int read()
{
    int x = 0,f = 1;char ch = getchar();
    for(;!isdigit(ch);ch = getchar())if(ch == '-')f = -f;
    for(;isdigit(ch);ch = getchar())x = 10 * x + ch - '0';
    return x * f;
}
const int maxn = 152600;
const DB inf = (DB)1.0 / 0.0;
int n,l,r;
LL a,b,c;
LL sum[maxn],f[maxn];
void force()
{
    for(int i=1;i<=n;i++)
    {
        for(int j=max(i-r,0);j<=min(i-l,n);j++)
            f[i] = max(f[i],f[j] + a * (sum[i] - sum[j]) * (sum[i] - sum[j]) + b * (sum[i] - sum[j]) + c);
    }
    cout<<f[n]<<endl;
}
struct point
{
    DB x,y;
    int id;
    point operator - (const point &b)const{return (point){x - b.x,y - b.y,id};}
    bool operator < (const point &b)const{return x < b.x;}
    DB operator * (const point &b)const{return x * b.y - y * b.x;}
}ps[maxn];
vector<int> Items[maxn << 2],ch[maxn << 2];
DB dp[maxn],Sum[maxn];
int q[maxn],top;
#define ls (x << 1)
#define rs ((x << 1) | 1)
void Insert(int x, int l, int r, int L, int R, int id)
{
    if(L <= l && r <= R){Items[x].push_back(id);return;}
    int mid = (l + r) >> 1;
    if(L <= mid) Insert(ls,l,mid,L,R,id);
    if(R > mid) Insert(rs,mid + 1,r,L,R,id);
}
inline DB trans(int frm,int targ){return (DB)dp[frm] + (DB)a * (Sum[targ] - Sum[frm]) * (Sum[targ] - Sum[frm]) + (DB)b * (Sum[targ] - Sum[frm]) + c;}
void CDQ(int x,int l,int r)
{
    int tp = 0;
    for(int i=0;i<Items[x].size();i++)
    {
        int to = Items[x][i];
        ps[++tp] = (point){Sum[to],dp[to] + (DB)a * Sum[to] * Sum[to] - (DB)b * Sum[to],to};
    }
    if(tp)
    {
        sort(ps + 1,ps + tp + 1);top = 0;
        q[++top] = 1;
        for(;q[1] <= tp && ps[q[1]].y == -inf;++q[1]);        
        for(int i=q[1]+1;i<=tp;i++)
        {
            if(ps[i].y == -inf) continue;
            for(;top > 1 && ((ps[q[top]] - ps[q[top-1]]) * (ps[i] - ps[q[top]])) >= 0;--top); 
            q[++top] = i;
        }
        if(q[1] > tp)top = 0;
        for(;top && ps[q[top]].y == -inf;--top);
        if(top)for(int i=1;i<=top;i++)ch[x].push_back(ps[q[i]].id);
    }
    if(l == r)
    {
        int now = x;
        while(now)
        {
            int nl = 0,nr = ch[now].size() - 1;
            while(nl < nr)
            {
                int md = (nl + nr) >> 1;
                if(md < (ch[now].size() - 1) && trans(ch[now][md],l) <= trans(ch[now][md+1],l))nl = md + 1;
                else nr = md;
            }
            if(ch[now].size())dp[l] = max(dp[l],trans(ch[now][nl],l));
            now = now >> 1;
        }
        return;
    }
    int mid = (l + r) >> 1;
    CDQ(ls,l,mid);CDQ(rs,mid + 1,r);
}
void solve()
{
    for(int i=1;i<=n;i++)Sum[i] = sum[i];
    for(int i=1;i<=n;i++)dp[i] = -inf;
    for(int i=0;i<=n;i++)Insert(1,0,n,i + l,min(i+r,n),i);
    //cout<<111<<endl;
    CDQ(1,0,n);LL ans = floor(dp[n]);
    printf("%lld\n",ans);
    //cout<<ret<<endl;
}
int main()
{
    freopen("paint.in","r",stdin);
    freopen("paint.out","w",stdout);
    n = read();a = read();b = read();c = read();l = read();r = read();
    for(int i=1;i<=n;i++)sum[i] = sum[i - 1] + read();
    //if(n <= 1000)force();
    //else
    solve();
}
T2

 

T3 route

樹上找一條鏈,滿足

1.鏈上最長邊和最短邊的差不超過 m

2.鏈上最短邊 $\times$ 鏈上邊權和 最大

$n,m \leq 2 \times 10^5$

sol:

如果沒有 m 的限制,就是一個點分治

對於分治中心往下的每一條鏈,我們記一個資訊 $(mi,su)$ 表示這條鏈上最小值為 $mi$ ,權值和為 $su$

按 $mi$ 的下標插入樹狀陣列,每次查詢即可

注意每次按兒子順序正著做一遍,反著做一遍·

這樣寫可以 $O(nlog^2n)$ 拿到 80 分

 

或者我們用 LCT 維護子樹資訊,很好想但。。。不太可寫

 

正解基於下面一個結論:如果樹上一個連通塊 u 的一條直徑為 $u_i,u_j$ ,一個連通塊 v 的一條直徑為 $v_i,v_j$

則 $(u_i,v_i),(u_i,v_j),(u_j,v_i),(u_j,v_j)$ 其中至少一個為連通塊 u,v 的並集的直徑

於是對於 80 分我們可以按從大到小加入每條邊然後直接統計答案

對於 100 分,除了加入,我們還有刪除操作

這個時候我們可以線段樹按時間分治,用一個可撤銷的並查集來維護連通性

或者 LCT

#include<cstdio>
#include<cctype>
#include<queue>
#include<ctime>
#include<cstring>
#include<algorithm>
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define ren for(int i=first[x];i;i=next[i])
using namespace std;
inline int read() {
    int x=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
typedef long long ll;
typedef pair<int,int> Pair;
const int maxn=160010;
struct Edge {
    int u,v,w;
    bool operator < (const Edge& ths) const {return w>ths.w;}
}E[maxn];
int n,lim,first[maxn],next[maxn<<1],dis[maxn<<1],to[maxn<<1],e;
void AddEdge(int u,int v,int w) {
    to[++e]=v;dis[e]=w;next[e]=first[u];first[u]=e;
    to[++e]=u;dis[e]=w;next[e]=first[v];first[v]=e;
}
ll mn[20][maxn*2],dep[maxn];
int pos[maxn*2],cnt;
void dfs(int x,int fa) {
     mn[0][++cnt]=dep[x];pos[x]=cnt;
     ren if(to[i]!=fa) {
         dep[to[i]]=dep[x]+dis[i];
         dfs(to[i],x);    
         mn[0][++cnt]=dep[x];
     }
}
int Log[maxn*2];
void init() {
     Log[0]=-1;rep(i,1,cnt) Log[i]=Log[i>>1]+1;
     for(int j=1;(1<<j)<=cnt;j++)
        for(int i=1;i+(1<<j)-1<=cnt;i++)
           mn[j][i]=min(mn[j-1][i],mn[j-1][i+(1<<j-1)]);     
}
ll query(int x,int y) {
    ll ans=dep[x]+dep[y];x=pos[x];y=pos[y];if(x>y) swap(x,y);
    int k=Log[y-x+1];
    return ans-2*min(mn[k][x],mn[k][y-(1<<k)+1]);    
}
Pair A[maxn];
Pair merge(Pair A,Pair B) {
    int x1=A.first,y1=A.second;
    int x2=B.first,y2=B.second;
    int x=x1,y=y1;
    if(query(x2,y2)>query(x,y)) x=x2,y=y2;
    if(query(x2,y1)>query(x,y)) x=x2,y=y1;
    if(query(x2,x1)>query(x,y)) x=x2,y=x1;
    if(query(y2,x1)>query(x,y)) x=y2,y=x1;
    if(query(y2,y1)>query(x,y)) x=y2,y=y1;
    return make_pair(x,y);
}
int pa[maxn],size[maxn];
int findset(int x) {return x==pa[x]?x:findset(pa[x]);}
int end[maxn];
struct Data {
    int x,y,pax,sizey;
    ll ansv;
    Pair datay;
}S[maxn];
int ToT;
ll maxlen,ans;
void link(int x,int y) {
    x=findset(x);y=findset(y);
    if(x==y) return;
    if(size[x]>size[y]) swap(x,y);
    S[++ToT]=(Data){x,y,pa[x],size[y],maxlen,A[y]};
    pa[x]=y;if(size[x]==size[y]) size[y]++;A[y]=merge(A[y],A[x]);
    maxlen=max(maxlen,query(A[y].first,A[y].second));
}
void restore(int begin) {
    while(ToT!=begin) {
        int x=S[ToT].x,y=S[ToT].y,pax=S[ToT].pax,sizey=S[ToT].sizey;
        maxlen=S[ToT].ansv;Pair z=S[ToT--].datay;
        pa[x]=pax;size[y]=sizey;A[y]=z;
    }
}
int ls[maxn<<1],rs[maxn<<1],rt;
void buildtree(int& o,int l,int r) {
    o=++ToT;if(l==r) return;
    int mid=l+r>>1;
    buildtree(ls[o],l,mid);buildtree(rs[o],mid+1,r);
}
int first2[maxn<<1],nxt2[maxn*20],id[maxn*20];
void AddMark(int x,int val) {
    id[++cnt]=val;nxt2[cnt]=first2[x];first2[x]=cnt;
}
void query(int o,int l,int r,int ql,int qr,int val) {
    if(ql<=l&&r<=qr) AddMark(o,val);
    else {
        int mid=l+r>>1;
        if(ql<=mid) query(ls[o],l,mid,ql,qr,val);
        if(qr>mid) query(rs[o],mid+1,r,ql,qr,val);
    }
}
void solve(int o,int l,int r) {
    int begin=ToT;
    for(int i=first2[o];i;i=nxt2[i]) link(E[id[i]].u,E[id[i]].v);
    if(l<r) {
        int mid=l+r>>1;
        solve(ls[o],l,mid);
        solve(rs[o],mid+1,r);
    }
    else ans=max(ans,maxlen*E[l].w);
    restore(begin);
}
int main() {
    freopen("route.in","r",stdin);
    freopen("route.out","w",stdout);
    n=read();lim=read();
    rep(i,1,n-1) {
        E[i].u=read();E[i].v=read();E[i].w=read();
        AddEdge(E[i].u,E[i].v,E[i].w);
    }
    buildtree(rt,1,n-1);ToT=0;
    dfs(1,0);init();cnt=0;
    sort(E+1,E+n);
    rep(i,1,n) pa[i]=i,A[i]=make_pair(i,i),size[i]=1;
    int j=n-1;
    dwn(i,n-1,1) {
        while(E[i].w-E[j].w>lim) j--;
        end[i]=j;query(rt,1,n-1,i,end[i],i);
    }
    solve(rt,1,n-1);
    printf("%lld\n",ans);
    return 0;
}
T3