2020牛客暑期多校训练营(第九场)

A Groundhog and 2-Power Representation

用栈模拟一下.
(为入栈)为出栈.

#include<bits/stdc++.h>
#define fi first
#define se second
#define lc (x<<1)
#define rc (x<<1|1)
#define gc getchar()//(p1==p2&&(p2=(p1=buf)+fread(buf,1,size,stdin),p1==p2)?EOF:*p1++)
#define mk make_pair
#define pii pair<int,int>
#define pll pair<ll,ll>
#define pb push_back
#define IT iterator 
#define vi vector<int>
#define TP template<class o>
#define SZ(a) ((int)a.size())
#define all(a) a.begin(),a.end()
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int N=188,M=2e4+10,size=1<<20,mod=998244353,inf=2e9;

//char buf[size],*p1=buf,*p2=buf;
template<class o> void qr(o &x) {
    char c=gc; x=0; int f=1;
    while(!isdigit(c)){if(c=='-')f=-1; c=gc;}
    while(isdigit(c)) x=x*10+c-'0',c=gc;
    x*=f;
}
template<class o> void qw(o x) {
    if(x/10) qw(x/10);
    putchar(x%10+'0');
}
template<class o> void pr1(o x) {
    if(x<0)x=-x,putchar('-');
    qw(x); putchar(' ');
}
template<class o> void pr2(o x) {
    if(x<0)x=-x,putchar('-');
    qw(x); putchar('\n');
}

char s[M];
const int A=(int)1e8;
struct rec {
    int a[N],len;
    rec(int x=0) {memset(a,0,sizeof a); a[1]=x; len=1;}
    void upd() {
        int &l=len;
        while(a[l+1]) {
            ++l;
            a[l+1] += a[l]/A;
            a[l] %= A;
        }
    }
    void operator +=(rec b) {
        for(int i=1;i<=b.len;i++) {
            a[i] += b.a[i];
            a[i+1] += a[i] / A;
            a[i] %= A;
        } 
        upd();
    }
    void operator +=(int x) {
        for(int i=1;i<=len&&x;i++) {
            a[i] += x;
            x=a[i]/A;
            a[i] -= x*A;
        }
    }
    void operator *=(int x) {
        for(int i=1;i<=len;i++) a[i]*=x;
        for(int i=1;i<=len;i++) {
            a[i+1] += a[i]/A;
            a[i] %= A;
        }
        upd();
    }
    int in(char *s) {
        char *IN=s;
        while(isdigit(*s)) 
            *this*=10,*this+=(*s++)-'0';
        return s-IN;
    }
    void out() {
        qw(a[len]);
        for(int i=len-1;i>0;i--) 
			printf("%08d",a[i]);
        puts("");
    }
} f[N*4],sta[M]; int top;

int main() {
    scanf("%s",s+1);
    f[0]=rec(1);
    for(int i=1;i<=640;i++) f[i]=f[i-1],f[i]*=2;
    for(int i=1;s[i];i++) {
        char c=s[i];
        if(c=='2'&&s[i+1]=='(') sta[++top]=rec(0),i++;
        else if(isdigit(c)) {
            rec t;
            int len=t.in(s+i); i+=len-1;
            sta[top] += t;
        } 
        else if(c==')'&&top) sta[top-1] += f[sta[top].a[1]],top--;
    }
    sta[0].out(); return 0;
}

B Groundhog and Apple Tree

想到了贪心微扰,但是没搞出来…

从起点休息好再走一定更方便.
实际上我们只要对每个点 i i i记录一下最小休息时间 f [ i ] f[i] f[i],收获 h p   g [ i ] hp~g[i] hp g[i].

那么对于 f [ i ] < g [ i ] f[i] < g[i] f[i]<g[i]的情况是会赚 h p hp hp的,我们令按 f [ i ] f[i] f[i]排序可以最优.(尽快得增大).
反过来,我们要让 f [ i ] ≥ g [ i ] f[i]\ge g[i] f[i]g[i]的情况从后往前删除时赚 h p hp hp也尽量多,所以按照 g [ i ] > g [ j ] g[i]>g[j] g[i]>g[j]排序.(在总和一定的情况下,这样我们可以让最低点尽量的高)

(实在巧妙…)

int n,fa[N];
ll f[N],g[N];//等待时间,收获hp
vector<int> e[N],w[N];
void add(int x,int y,int z) {e[x].pb(y);w[x].pb(z);  }
bool cmp(int x,int y) {
    if(f[x]<g[x]) {
        if(f[y]<g[y]) return f[x]<f[y];
        return 1;
    }
    else {
        if(f[y]>=g[y]) return g[x]>g[y];
        return 0;
    }
}

void dfs(int x) {
    int sz=SZ(e[x]);
    for(int i=0;i<sz;i++) {
        int y=e[x][i],z=w[x][i];
        if(y==fa[x]) continue;
        fa[y]=x; dfs(y); 
        f[y]+=z; g[y]-=z;
        if(g[y]<0) f[y] -= g[y],g[y]=0;
    }
    sort(all(e[x]),cmp);
    f[x]=0;
    for(int y:e[x]) if(y^fa[x]) {
        g[x] -= f[y];
        if(g[x] < 0) f[x] -= g[x],g[x]=0;
        g[x] += g[y];
    }
}

int main() {
    int T; qr(T); while(T--) {
        qr(n);
        for(int i=1;i<=n;i++) e[i].clear(),w[i].clear(),qr(g[i]);
        for(int i=1,x,y,z;i<n;i++) qr(x),qr(y),qr(z),add(x,y,z),add(y,x,z);
        f[1]=fa[1]=0; dfs(1); pr2(f[1]);
    }
}

C Groundhog and Gaming Time

我们称读入进来的区间为线段.
后面的"区间" 和 “线段” 的含义不同.
2 n 2n 2n个点划分成 2 n − 1 2n-1 2n1个最小的区间.
答案等价于每个区间的长度乘上 包含该区间的线段的交的长度期望 .
L i L_i Li表示第 i i i个最小区间,则有:
a n s = ∑ i = 1 2 n − 1 ∣ L i ∣ ⋅ E ( L i ) ans=\sum_{i=1}^{2n-1}|L_i|\cdot E(L_i) ans=i=12n1LiE(Li).
其中 E ( L i ) = ∑ j = 1 2 n − 1 2 c o v e r j − 1 2 n L j = ∑ j = 1 2 n − 1 2 c o v e r j 2 n L j − ∑ L 2 n , 其 中 c o v e r j 表 示 能 同 时 覆 盖 L i , L j 的 线 段 数 量 E(L_i)=\sum_{j=1}^{2n-1} \dfrac{2^{cover_j}-1}{2^n} L_j=\sum_{j=1}^{2n-1}\dfrac{2^{cover_j}}{2^n}L_j-\dfrac{\sum L}{2^n},其中cover_j表示能同时覆盖L_i,L_j的线段数量 E(Li)=j=12n12n2coverj1Lj=j=12n12n2coverjLj2nL,coverjLi,Lj线.
然后我们用线段树维护 2 c o v e r j L j 2^{cover_j}L_j 2coverjLj即可.(具体就是实现区间乘区间求和)

int n,m,l[N],r[N],b[N];
int find(int x) {return lower_bound(b+1,b+m+1,x)-b;}

ll ans,inv=(mod+1)/2;

ll val[N<<2],t[N<<2];

void upd(int x) {val[x]=(val[lc]+val[rc])*t[x]%mod; }

void bt(int x,int l,int r) {
    val[x]=b[r+1]-b[l]; t[x]=1;
    if(l == r) return ;
    int mid=(l+r)/2;
    bt(lc,l,mid);
    bt(rc,mid+1,r);
}

void mul(int x,int l,int r,int L,int R,int d) {
    if(L<=l&&r<=R) {
        val[x] = val[x] * d % mod;
        t[x] = t[x] * d % mod;
        return ;
    }
    int mid=(l+r)/2;
    if(L<=mid) mul(lc,l,mid,L,R,d);
    if(mid< R) mul(rc,mid+1,r,L,R,d);
    upd(x);
}

struct rec {int x,y,z; }; vector<rec> ad[N];

int main() {
    qr(n); b[m=1]=0; b[++m]=inf;
    for(int i=1;i<=n;i++) {
        qr(l[i]); qr(r[i]);
        b[++m]=l[i];
        b[++m]=++r[i];
    }
    sort(b+1,b+m+1); m=unique(b+1,b+m+1)-(b+1);
    for(int i=1;i<=n;i++) {
        l[i]=find(l[i]); r[i]=find(r[i]);
        rec tmp=rec{l[i],r[i]-1,2};
        ad[l[i]].pb(tmp);
        tmp.z=inv;
        ad[r[i]].pb(tmp);
    }
    bt(1,1,m-1);
    for(int i=1;i<m;i++) {
        for(rec p:ad[i])
            mul(1,1,m-1,p.x,p.y,p.z);
        ans = (ans+(b[i+1]-b[i])*val[1])%mod;
    }
    ans=(ans+mod-(ll)inf*inf%mod)%mod;
    for(int i=1;i<=n;i++) ans=ans*inv%mod;
    pr2(ans); return 0;
}

D Groundhog and Golden Apple

i i i计算答案时,把所有包含 i i i的边加入.
k i = 0 k_i=0 ki=0,则 a n s = ans= ans=当前连通块大小.
否则, a n s ans ans还要加上相邻连通块的大小.

运用线段树+可撤销并查集可以做到 O ( n log ⁡ 2 n ) O(n\log^2n) O(nlog2n).
处理的细节在于处理相邻连通块的大小.
如果我们每更新一个连通块就把相邻联通块都更新,那么一定会 T T T.
我们可以让贡献具有方向性,比如:
定义一个连通块的顶点为原树内深度最小的点,相邻两块由深度划分为父亲连通块,儿子连通块.
我们只要让儿子连通块更新父亲联通块的相邻连通块大小之和即可.(最后只要查一下父亲连通块即可).


int n,K[N],f[N],fa[N],s1[N],s2[N],tp[N],ans[N];
//f:原树父亲,fa:并查集信息,s1:并查集大小,s2:深度较大的相邻块的并查集+自身的大小和.tp:并查集顶点.

int get(int x) {while(x^fa[x]) x=fa[x]; return x;}

struct E {int x,y; } e[N];
struct edge{int y,next; } a[N*2]; int len,last[N];
void ins(int x,int y) {a[++len]=(edge){y,last[x]}; last[x]=len; }

void dfs(int x) {
    s1[x]=s2[x]=1;
    tp[x]=fa[x]=x;
    for(int k=last[x],y;k;k=a[k].next)
        if((y=a[k].y)^f[x]) f[y]=x,dfs(y),s2[x]++;
}

vector<int> v[N<<2];
void add(int x,int l,int r,int L,int R,int d) {
    if(L<=l&&r<=R) return v[x].pb(d);
    int mid=(l+r)/2;
    if(L<=mid) add(lc,l,mid,L,R,d);
    if(mid< R) add(rc,mid+1,r,L,R,d);
}

struct node {
    int x,y,s1x,s2x,tpx,F,s2F;
} sta[N]; int top;

void del(int now) {
    while(top>now) {
        node &t=sta[top--];
        int x=t.x,y=t.y;
        fa[y]=y;
        s1[x]=t.s1x;
        s2[x]=t.s2x;
        tp[x]=t.tpx;
        s2[t.F]=t.s2F;
    }
}

void solve(int x,int l,int r) {
    int in=top;
    for(int k:v[x]) {
        int u=e[k].x,v=e[k].y,F;
        if(fa[u] == v) swap(u,v);
        u=get(u); v=get(v); F=get(f[tp[u]]);
        if(s1[u]>=s1[v]) {
            sta[++top]=(node){u,v,s1[u],s2[u],tp[u],F,s2[F]};
            fa[v] = u;
            s2[F] += s1[v];
            s2[u] += s2[v] - s1[v];
            s1[u] += s1[v];
        }
        else {
            sta[++top]=(node){v,u,s1[v],s2[v],tp[v],F,s2[F]};
            fa[u] = v;
            s2[F] += s1[v];
            s2[v] += s2[u] - s1[v];
            s1[v] += s1[u];
            tp[v] = tp[u];
        }
    }
    if(l == r) {
        x=get(l);
        if(!K[l]) ans[l]=s1[x];
        else ans[l]=s2[x]+s1[get(f[tp[x]])];
    }
    else {
        int mid=(l+r)/2;
        solve(lc,l,mid);
        solve(rc,mid+1,r);
    }
    del(in);
}

int main() {
    qr(n);
    for(int i=1;i<=n;i++) qr(K[i]);
    for(int i=1,x,y,l,r;i<n;i++) {
        qr(x); qr(y); e[i]=(E){x,y}; qr(l); qr(r);
        ins(x,y); ins(y,x); add(1,1,n,l,r,i);
    }
    dfs(1); solve(1,1,n);
    for(int i=1;i<=n;i++) pr2(ans[i]);
    return 0;
}

E Groundhog Chasing Death

∏ i = a b ∏ j = c d gcd ⁡ ( x i , y j ) \prod_{i=a}^b \prod_{j=c}^d\gcd(x^i,y^j) i=abj=cdgcd(xi,yj).

考虑每个质因子 p p p的指数.
X , Y X,Y X,Y分别表示 x , y x,y x,y p p p的指数.
a n s = ∏ p p ∑ i = a b ∑ j = c d min ⁡ ( i X , j Y ) ans=\prod_{p} p^{\sum_{i=a}^b \sum_{j=c}^d \min(iX,jY)} ans=ppi=abj=cdmin(iX,jY).

直接 f o r for for第一维,然后后面 j j j可以分成两段考虑.


ll a,b,c,d,x,y,ans=1;

ll power(ll a,ll b) {
    ll c=1;
    while(b) {
        if(b&1)c=c*a%mod;
        b /= 2;a=a*a%mod;
    }
    return c;
}

ll f(ll x,ll y) {
    return x<=y?(x+y)*(y-x+1)/2%mo:0;
}

void solve(int p) {
    int u=0,v=0;
    while(x%p==0) x/=p,u++;
    while(y%p==0) y/=p,v++;
    if(u>0 && v>0) {
        ll e=0;
        for(ll j=a;j<=b;j++) {
            ll t=j*u/v;
            if(t<c) e += (d-c+1)*j*u%mo;
            else if(t>=d) e += f(c,d)*v;
            else e += f(c,t)*v%mo+(d-t)*j*u%mo;
            e %= mo;
        }
        ans = ans*power(p,e)%mod;
    }
}

int main() {
    qr(a); qr(b); qr(c); qr(d); qr(x); qr(y);
    for(int i=2;i*i<=max(x,y);i++) solve(i);
    if(x>1&&y>1) solve(x);
    return pr2(ans),0;
}

F Groundhog Looking Dowdy

套路题.
如果我们钦定最小的数 l l l,那么我们要找到一个最小的数 r r r,满足 [ l , r ] [l,r] [l,r]内包含的天数 > = m >=m >=m.
树状数组维护每一天的第一个位置即可.(实际上只要用双指针即可.)

int n,m,pos[N],R[N],s;
struct rec {
    int x,id;
    bool operator <(rec b) const {return x<b.x;} 
} a[N]; int tot;

int c[N];
void add(int x,int d) {for( ;x<=tot;x+=x&-x) c[x] += d;}
int ask() {
    int x=0,y=m;
    for(int i=21;i>=0;i--)
        if((1<<i)+x<tot&&c[(1<<i)+x]<y)
            x += 1<<i,y -= c[x];
    return a[x+1].x;
}

int main() {
    qr(n); qr(m);
    for(int i=1,k;i<=n;i++) {
        qr(k); 
        while(k--) 
            qr(a[++tot].x),a[tot].id=i;
    }
    sort(a+1,a+tot+1); s=n;//总剩余种类
    for(int i=tot;i;i--) {
        R[i]=pos[a[i].id];
        pos[a[i].id]=i;
    }
    int ans=inf;
    for(int i=1;i<=n;i++) add(pos[i],1);
    for(int i=1;i<=tot&&s>=m;i++) {
        ans=min(ans,ask()-a[i].x);
        add(i,-1);
        if(!R[i]) s--;
        else add(R[i],1);
    }
    pr2(ans);
}

G Groundhog Playing Scissors

多边形旋转等价于直线旋转,直线旋转等价于直线的垂线在旋转.
我们可以把角度划分为若干个小单元,然后对每个角度计算答案.

const int N=3e5+10,size=1<<20,mod=998244353,inf=2e9;
const ld PI=acos(-1.0);

int n;

struct P {
    db x,y;
    P(db a=0,db b=0) {x=a; y=b; }
    P operator +(P b) const {return P(x+b.x,y+b.y);}
    P operator -(P b) const {return P(x-b.x,y-b.y);}
    P operator *(db t) const {return P(x*t,y*t); }//数乘
    db operator *(P b) const {return x*b.y-y*b.x; }//叉积
    db operator ^(P b) const {return x*b.x+y*b.y; }//点积
    void input() {scanf("%lf %lf",&x,&y); }
    db length() {return sqrt(x*x+y*y); }
    P dot() const {return P(-y,x); }
} a[N];

struct L {
    P a,b;
    L(P u,P v):a(u),b(v){};
    L(){};
    void input() {a.input(); b.input(); }
    db mult(P t) const {//叉积
        P u=a-t,v=b-t;
        return u*v;
    }
    db length() {return (a-b).length(); }
    db dis(P t) {//点到直线的距离
        return abs(mult(t))/length();
    }
    bool pd(L t) const {//判断线段是否相交
        return (mult(t.a)>=0) != (mult(t.b)>=0);
    }
    P jd(L t) const {//交点
        db u=mult(t.a),v=-mult(t.b);
        return t.a*(v/(u+v))+t.b*(u/(u+v));
    }
} s, t;

int main() {
    scanf("%d", &n);
    for(int i=1;i<=n;i++) {
        a[i].input();
        a[i+n]=a[i+2*n]=a[i];//求相交的两边.
    }
    db len,d; scanf("%lf",&len); s.input(); d=s.dis(P(0,0));
    int tot=3e5,l=1,r=1,cnt=0;
    for(int i=0;i<tot;i++) {//旋转多边形等价于旋转直线,旋转直线等价于直线的垂线在旋转
        P x=P(cos(2*PI/tot*i),sin(2*PI/tot*i))*d;//垂线.
        P y=x.dot(),z=x+y,u,v;//y为线段对应的向量,(x,z)组成原来的直线.
    	t=L(x,z);
        while(1) {
            if(t.pd(L(a[l],a[l+1]))) {u=t.jd(L(a[l],a[l+1])); break;}
            l++;
        }
        r=max(l+1,r);
        while(1) {
            if(t.pd(L(a[r],a[r+1]))) {v=t.jd(L(a[r],a[r+1])); break; }
            r++;
        }
        if((u-v).length()>len) cnt++;
    }
    printf("%.4lf\n",1.0*cnt/tot); return 0;
}

H Groundhog Speaking Groundhogish

容易想到 O ( n k ) O(nk) O(nk)的暴力DP.
可以发现这是一个分组完全背包的形式,可以考虑用生成函数计算.

namespace P {
    const int g=3,inv2=(mod+1)/2;
    int R[N],w[N];
    void upd(int &x) {x+=x>>31&mod;}
    ll power(ll a,ll b=mod-2) {
        ll c=1;
        while(b) {
            if(b&1) c=c*a%mod;
            b /= 2; a=a*a%mod;
        }
        return c;
    }
    int calc(int x) {if((x&-x)==x) return x; int n=1; while(n<x) n*=2; return n;}//输入长度 
    void init(int m) {
        int n=calc(m); 
        for(int i=1;i<n;i*=2) {//枚举半区间长度,把对应的单位根填入w数组 
            ll t=power(g,(mod-1)/(2*i)),d=1;
            for(int j=0;j<i;j++) w[i+j]=d,d=d*t%mod;
        }
    }
    int pre(int m) {//输入总长度 
        int n=calc(m);
        for(int i=1;i<n;i++) R[i]=(R[i>>1]>>1)|(i&1?n>>1:0);
        return n;
    }
    void DFT(int *f,int n) {
        static ull p[N];
        for(int i=0;i<n;i++) p[R[i]]=f[i];
        for(int i=1,t;i<n;i*=2) for(int j=0;j<n;j+=2*i)
            for(int k=0;k<i;k++) t=p[j+k+i]*w[i+k]%mod,p[j+k+i]=p[j+k]+mod-t,p[j+k]+=t;
        for(int i=0;i<n;i++) f[i]=p[i]%mod;
    }
    void IDFT(int *f,int n) {
        reverse(f+1,f+n); DFT(f,n); ll inv=power(n);
        for(int i=0;i<n;i++) f[i]=inv*f[i]%mod;
    }
    void copy(int *a,int *b,int n) {memcpy(a,b,sizeof(int)*n);}
    void clear(int *a,int len) {memset(a+len,0,sizeof(int)*len);}
    void clear(int *a,int x,int y) {if(x<y) memset(a+x,0,sizeof(int)*(y-x));}
    void mult(int *a,int *b,int n,int m) {
        static int c[N],d[N];
        int x=pre(n+m-1);
        copy(c,a,n); clear(c,n,x);
        copy(d,b,m); clear(d,m,x);
        DFT(c,x); DFT(d,x);
        for(int i=0;i<x;i++) c[i]=(ll)c[i]*d[i]%mod;
        IDFT(c,x);
        for(int i=0;i<n+m-1;i++) a[i]=c[i];
    }
    int h[N];
	void getinv(int *a,int *b,int n) {// 
		int m=calc(n);
		clear(a,n,m*2); n=m;
		clear(b,0,2*n); clear(h,0,2*n); b[0]=power(a[0]);
		for(int p=2;p<=n;p*=2) {
			int x=pre(p*2);
			copy(h,a,p); clear(h,p); DFT(h,x); DFT(b,x);
			for(int i=0;i<x;i++) b[i]=1LL*(2-1LL*b[i]*h[i]%mod+mod)*b[i]%mod;
			IDFT(b,x); clear(b,p);
		}
	}
}

int n,m,k,a[N],f[N*2],*pos[N],*p=f,sz[N],sum,b[N],inv[N];

void solve(int l,int r) {
    if(l == r) return;
    int mid=(l+r)/2;
    solve(l,mid);
    solve(mid+1,r);
    P::mult(pos[l],pos[mid+1],sz[l],sz[mid+1]);
    sz[l] += sz[mid+1]-1; 
}

int main() {
    P::init(1<<18);
    while(qr(n),qr(m),qr(k),n) {
        p=f; sum=0; a[n+1]=0;
        for(int i=1;i<=n;i++) qr(a[i]);
        for(int i=1;i<=m;i++) qr(b[i]),sum=(sum+b[i])%mod;
        ll t=1;
        for(int i=1;i<=n;i++) t=t*b[a[i]]%mod;
        for(int i=1;i<=n+1;i++) {
            pos[i]=p;  sz[i]=2;
            *p++=1; *p++=(mod-sum+b[a[i]])%mod;
        }
        solve(1,n+1); 
        int len=min(k+1,n+2);
        for(int i=len;i<=2*n+2;i++) f[i]=0;
        P::getinv(f,inv,k+1);
        memset(f,0,sizeof(int)*(p-f));
        ll ans=0;
        for(int i=0;i<=k;i++) ans=(ans+inv[i])%mod;
        pr2(ans*t%mod);
    }
}

I The Crime-solving Plan of Groundhog

贪心找最小的数出来.其他部分为另一个数.

int n,a[N];

struct rec {
    int a[N],len;
    rec(int *b) {
        for(int i=n;i;i--) a[i]=b[i];
        len=n-1;
    }
    rec() {}
    void operator *=(int x) {
        for(int i=1;i<=len;i++)
            a[i] *= x;
        for(int i=1;i<=len;i++)
            a[i+1] += a[i]/10,a[i] %= 10;
        if(a[len+1]) len++;
    }
    void out() {
        for(int i=len;i;i--) putchar(a[i]+'0');
        puts("");
    }
} f;

int main() {
    int T; qr(T); while(T--) {
        qr(n);
        for(int i=1;i<=n;i++) qr(a[i]);
        sort(a+1,a+n+1,greater<int>());
        int pos=n;
        while(!a[pos]) pos--;
        int v=a[pos];
        for(int i=pos+1;i<=n;i++) a[i-1]=a[i];
        a[n]=0;
        if(!a[n-1]) swap(a[n-1],a[pos-1]);
        f=rec(a); f*=v; f.out();
    }
    return 0;
}

J The Escape Plan of Groundhog

我们先确定上下行边界.
那么合法的一段就是有上下有连续的1.
然后我们做一个内部权值的前缀和,当一个列 k k k合法时,给桶加入前缀和 s [ k ] s[k] s[k].
这样我们就可以通过查桶找到有多少个合法的列和他围成一个合法的子矩阵.
总复杂度为 O ( n 2 m ) O(n^2m) O(n2m).

int n,m,a[N][N],b[N][N],s[N],f[M*2];
ll ans;

int main() {
    qr(n); qr(m); 
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++) {
            qr(a[i][j]);
            if(!a[i][j]) a[i][j]--;
            b[i][j]=b[i-1][j] + a[i][j];
        }
    s[0]=M;
    for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++) {
            int pre=1;
            for(int k=1;k<=m;k++) {
                if(a[i][k] != 1 || a[j][k] != 1) {
                    for(int l=pre;l<k;l++)
                        if(b[j][l]-b[i-1][l] == j-i+1)
                            f[s[l]]--;
                    pre=k+1; s[k]=M; continue;
                }
                s[k]=s[k-1]+b[j-1][k]-b[i][k];
                if(b[j][k]-b[i-1][k] == j-i+1) {
                    ans += f[s[k-1]]+f[s[k-1]+1]+f[s[k-1]+1];
                    f[s[k]]++;
                }
            }
            for(int l=pre;l<=m;l++) 
                if(b[j][l]-b[i-1][l] == j-i+1) 
                    f[s[l]]--;
        }
    pr2(ans); return 0;
}

K The Flee Plan of Groundhog

D F S DFS DFS.

int n,m,fa[N],dep[N];
struct edge{int y,next; } a[N]; int len,last[N];
void ins(int x,int y) {a[++len]=(edge){y,last[x]}; last[x]=len; }

void dfs(int x) {
    for(int k=last[x],y;k;k=a[k].next)
        if((y=a[k].y)^fa[x]) {
            fa[y]=x; dep[y]=dep[x]+1;
            dfs(y);
        }
}

int DFS(int x,int d) {
    if(d<=0) return 0;
    int res=(d+1)/2;
    for(int k=last[x],y;k;k=a[k].next) 
        if((y=a[k].y)^fa[x]) {
            fa[y]=x;
            res=max(res,1+DFS(y,d+(dep[y]<dep[x]?-3:-1)));
        }
    return res;
}

int main() {
    qr(n);  qr(m);
    for(int i=1,x,y;i<n;i++) qr(x),qr(y),ins(x,y),ins(y,x);
    dfs(n); int root=1; for(int i=1;i<=m;i++) root=fa[root];
    fa[root]=0; pr2(DFS(root,dep[root])); return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Infinite_Jerry

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值