In mathematics you don't understand things, you just get used to them.

标签 brute force 下的文章

本来十分抗拒,但 GM 强制。

「ABC 183A」ReLU

Link.

略。

#include<cstdio>
int main()
{
    long long n;
    scanf("%lld",&n);
    printf("%lld\n",n>0?n:0);
    return 0;
}

「ABC 183B」Billiards

Link.

设答案坐标 $A(m,n)$,然后算出 $y_{AG}$ 解析式,再带 $x=S'_{x}$,$S'$ 是 $S$ 关于直线 $x=m$ 的对称点,得出来的 $y$ 要等于 $n$,然后列个方程解出来答案为 $\frac{S_{x}G_{y}+G_{x}S_{y}}{S_{y}+G_{y}}$。

#include<cstdio>
double sx,sy,gx,gy;
int main()
{
    scanf("%lf%lf%lf%lf",&sx,&sy,&gx,&gy);
    printf("%lf\n",(sx*gy+gx*sy)/(sy+gy));
    return 0;
}

「ABC 183C」Travel

Link.

全排列枚举算答案即可。

#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
vector<int> per;
int n,ans;
long long k,tim[20][20];
int main()
{
    scanf("%d%lld",&n,&k);
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=n;++j)    scanf("%lld",&tim[i][j]);
    }
    per.resize(n+2);
    for(int i=1;i<=n;++i)    per[i]=i;
    per[n+1]=1;
    do
    {
        long long sum=0;
        for(int i=2;i<=n+1;++i)    sum+=tim[per[i-1]][per[i]];
        if(sum==k)    ++ans;
    }while(next_permutation(per.begin()+2,per.end()-1));
    printf("%d\n",ans);
    return 0;
}

「ABC 183D」Water Heater

Link.

前缀和。

#include<cstdio>
int n,s[200010],t[200010],p[200010],w;
long long dif[200010];
int main()
{
    scanf("%d%d",&n,&w);
    for(int i=1;i<=n;++i)    scanf("%d%d%d",&s[i],&t[i],&p[i]);
    for(int i=1;i<=n;++i)
    {
        dif[s[i]]+=p[i];
        dif[t[i]]-=p[i];
    }
    for(int i=1;i<=200000;++i)    dif[i]+=dif[i-1];
    for(int i=0;i<=200000;++i)
    {
        if(dif[i]>w)
        {
            printf("No\n");
            return 0;
        }
    }
    printf("Yes\n");
    return 0;
}

「ABC 183E」Water Heater

Link.

递推完了。

#include<cstdio>
const int mod=1e9+7;
long long ans;
int n,m,mp[2010][2010],row[2010],col[2010],dia[5010];
char str[2010];
int add(long long a,long long b)
{
    if(a+b>=mod)    return (a+b)%mod;
    else    return a+b;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)
    {
        scanf("%s",str+1);
        for(int j=1;j<=m;++j)
        {
            if(str[j]=='.')    mp[i][j]=0;
            else    mp[i][j]=1;
        }
    }
    int lay=2e3;
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=m;++j)
        {
            if(mp[i][j])
            {
                row[i]=0;
                col[j]=0;
                dia[i-j+lay]=0;
            }
            else
            {
                int tmp=add(add(row[i],col[j]),dia[i-j+lay]);
                if(i==1&&j==1)    ++tmp;
                row[i]=add(row[i],tmp);
                col[j]=add(col[j],tmp);
                dia[i-j+lay]=add(dia[i-j+lay],tmp);
                ans=tmp;
            }
        }
    }
    printf("%d\n",ans);
    return 0;
}

「ABC 183F」Confluence

Link.

并查集板。

#pragma GCC diagnostic error "-std=c++11"
#include<map>
#include<cstdio>
using namespace std;
map<int,int> mp[200010];
int n,m,fa[200010];
void makeset()
{
    for(int i=1;i<=n;++i)    fa[i]=i;
}
int findset(int x)
{
    if(x^fa[x])    fa[x]=findset(fa[x]);
    return fa[x];
}
void mergeset(int x,int y)
{
    x=findset(x);
    y=findset(y);
    if(x^y)
    {
        if(mp[x].size()>mp[y].size())
        {
            fa[y]=x;
            for(auto p:mp[y])    mp[x][p.first]+=p.second;
        }
        else
        {
            fa[x]=y;
            for(auto p:mp[x])    mp[y][p.first]+=p.second;
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    makeset();
    for(int i=1;i<=n;++i)
    {
        int x;
        scanf("%d",&x);
        mp[i][x]++;
    }
    while(m--)
    {
        int opt,opx,opy;
        scanf("%d%d%d",&opt,&opx,&opy);
        if(opt==1)    mergeset(opx,opy);
        else
        {
            int tmp=findset(opx);
            printf("%d\n",mp[tmp][opy]);
        }
    }
    return 0;
}

Description

Link.

给一个树,$n$ 个点,有点权,初始根是 1。

$m$ 个操作,种类如下:

1 x 将树根换为 $x$。

2 x y 给出两个点 $x,y$,从 $x$ 的子树中选每一个点,$y$ 的子树中选每一个点,求点权相等的情况数。

Solution

我首先认为这是 SNOI2017 一个简单的询问 搬到树上。

我们传统地把此题分为两个 $\texttt{pass}$,一个询问,一个修改。

  • $\texttt{pass 1}$:询问

我直接按 一个简单的询问 的方法讲。其实是把以前的题解 copy 过来了

由于是出现次数,满足区间加减性,所以我们可以这样表达 $\mathrm{get}(l,r,x)$(省略 $x$):

$$ \mathrm{get}(l,r)=\mathrm{get}(1,r)-\mathrm{get}(1,l-1) $$

那么我们代进原式,化一波式子($\mathrm{get}(p)=\mathrm{get}(1,p,x)$):

$$ \sum_{i=1}^{\infty}\mathrm{get}(l_{1},r_{1},x)\times\mathrm{get}(l_{2},r_{2},x) $$

$$ \sum_{i=1}^{\infty}(\mathrm{get}(1,r_{1})-\mathrm{get}(1,l_{1}-1))\times(\mathrm{get}(1,r_{2})-\mathrm{get}(1,l_{2}-1)) $$

$$ \sum_{i=1}^{\infty}\mathrm{get}(r_{1})\times\mathrm{get}(r_{2})-\mathrm{get}(r_{1})\times\mathrm{get}(l_{2}-1)-\mathrm{get}(l_{1}-1)\times\mathrm{get}(r_{2})+\mathrm{get}(l_{1}-1))\times\mathrm{get}(l_{2}-1) $$

$$ \mathrm{let}\ F(x,y)=\sum_{d}\mathrm{get}(1,l,d)\times\mathrm{get}(1,r,d) $$

则答案为:

$$ F(r_{1},r_{2})-F(r_{1},l_{2}-1)-F(l_{1}-1,r_{2})+F(l_{1}-1,l_{2}-1) $$

考虑怎么更新,比如从 $l$ 更新到 $l+1$,则:

$$ \mathrm{get(1,l)}\times\mathrm{get}(1,r) $$

$$ \mathrm{get(1,l+1)}\times\mathrm{get}(1,r) $$

$$ \mathrm{get(1,l)}\times\mathrm{get}(1,r)+\mathrm{cont}(a_{l}) $$

其中 $\mathrm{cont}(a_{l})$ 表示 $a_{l}$ 的出现次数。

则我们就知道怎么更新了,由于我们维护和的是前缀信息,所以姿势和普通莫队有点不一样。

维护两个数组 cntl[x]cntr[y] 表示答案式子

$$ F(r_{1},r_{2})-F(r_{1},l_{2}-1)-F(l_{1}-1,r_{2})+F(l_{1}-1,l_{2}-1) $$

子树的话直接 DFS 序拍到序列上。

  • $\texttt{pass 2}$:修改

现在我们面临着查询操作我们是用莫队整的,但这个修改貌似不单纯。其实也是从树剖模板缝合过来的

分类讨论,设我们当前要换的根为 $rt$,现在来处理询问,设查询的节点为 $u$,$\text{LCA}(u,v)$ 为节点 $u$ 和节点 $v$ 的最近公共祖先。

    • 如果 $rt=u$,则我们直接对整棵树进行查询。
    • 如果 $\text{LCA}(u,rt)\neq u$,此时修改不影响查询。
    • 如果 $\text{LCA}(u,rt)=u$,此时 $rt$ 在 $u$ 的子树里,那么需要查询的地方就很明确了,后面的步骤显然。

于是我们不需要实际的去处理这个修改,然后就可以直接莫队了。

(整体感觉是个 原题+假上树+树剖模板 的缝合题)

/* Clearink */

#include <cstdio>
#include <algorithm>

using namespace std;
typedef long long LL;

const int MAXN = 5e5 + 5, MAXM = 1e6 + 5;

int rint () {
    int x = 0, f = 1; char c = getchar ();
    for ( ; c < '0' || c > '9'; c = getchar () )    f = c == '-' ? -1 : f;
    for ( ; c >= '0' && c <= '9'; c = getchar () )    x = ( x << 3 ) + ( x << 1 ) + ( c & 15 );
    return x * f;
}

template<class _T>
void wint ( _T x ) {
    if ( x < 0 )    putchar ( '-' ), x = ~ x + 1;
    if ( x > 9 )    wint ( x / 10 );
    putchar ( x % 10 ^ '0' );
}

template<class _T> void swapp ( _T& x, _T& y ) { _T w = x; x = y; y = w; }

struct GraphSet {
    int to, nx;
    GraphSet () : to ( 0 ), nx ( 0 ) {}
    GraphSet ( const int a, const int b ) : to ( a ), nx ( b ) {}
} asg[MAXN * 2];

struct Quest {
    int l, r, ID, x;
    Quest () : l ( 0 ), r ( 0 ), ID ( 0 ), x ( 0 ) {}
    Quest ( const int a, const int b, const int c, const int d ) : l ( a ), r ( b ), ID ( c ), x ( d ) {}
} asq[MAXM * 8], itls[MAXN];

LL cur = 0, ans[MAXM], buc1[MAXN], buc2[MAXN];
int rt, pos[MAXN], blo = 320, col[MAXN], freq;
int n, m, bgn[MAXN], cnt, sjc, segl[MAXN], segr[MAXN], kfa[MAXN][21], a[MAXN], dept[MAXN], pri[MAXN], len;

void addE ( const int u, const int v ) { asg[++ cnt] = GraphSet ( v, bgn[u] ), bgn[u] = cnt; }
bool existcmp ( const Quest& one, const Quest& ano ) { return pos[one.l] == pos[ano.l] ? one.r < ano.r : one.l < ano.l; }

void dfs ( const int u, const int lst ) {
    kfa[u][0] = lst, dept[u] = dept[lst] + 1;
    segl[u] = ++ sjc, col[sjc] = a[u];
    for ( int i = 1; i <= 20; ++ i )    kfa[u][i] = kfa[kfa[u][i - 1]][i - 1];
    for ( int i = bgn[u]; i; i = asg[i].nx ) {
        int v = asg[i].to;
        if ( v == lst )    continue;
        dfs ( v, u );
    }
    segr[u] = sjc;
}

int calcKAC ( int u, int k ) {
    for ( int i = 20; ~ i; -- i ) {
        if ( k >= ( 1 << i ) )    k -= ( 1 << i ), u = kfa[u][i];
    }
    return u;
}

int calcLCA ( int u, int v ) {
    if ( dept[u] < dept[v] )    swapp ( u, v );
    for ( int i = 20; ~ i; -- i ) {
        if ( dept[kfa[u][i]] >= dept[v] )    u = kfa[u][i];
    }
    if ( u == v )    return u;
    for ( int i = 20; ~ i; -- i ) {
        if ( kfa[u][i] != kfa[v][i] )    u = kfa[u][i], v = kfa[v][i];
    }
    return kfa[u][0];
}

void initial () {
    for ( int i = 1; i <= n; ++ i )    pos[i] = ( i - 1 ) / blo + 1;
    sort ( pri + 1, pri + 1 + n );
    len = unique ( pri + 1, pri + 1 + n ) - pri - 1;
    for ( int i = 1; i <= n; ++ i )    a[i] = lower_bound ( pri + 1, pri + 1 + len, a[i] ) - pri;
    dfs ( 1, 0 );
}

void splitASdrug ( const int u, int& ils ) {
    if ( u == rt )    itls[++ ils] = Quest ( 1, n, 0, 0 );
    else {
        int lca = calcLCA ( u, rt );
        if ( lca != u )    itls[++ ils] = Quest ( segl[u], segr[u], 0, 0 );
        else {
            int ar = calcKAC ( rt, dept[rt] - dept[u] - 1 );
            if ( 1 <= segl[ar] - 1 )    itls[++ ils] = Quest ( 1, segl[ar] - 1, 0, 0 );
            if ( segr[ar] + 1 <= n )    itls[++ ils] = Quest ( segr[ar] + 1, n, 0, 0 );
        }
    }
}

void transASsub ( const int l1, const int r1, const int l2, const int r2, const int ID ) {
    asq[++ m] = Quest ( r1, r2, ID, 1 ), asq[++ m] = Quest ( r1, l2 - 1, ID, -1 );
    asq[++ m] = Quest ( l1 - 1, r2, ID, -1 ), asq[++ m] = Quest ( l1 - 1, l2 - 1, ID, 1 );
}

void transASmany ( const int l, const int r ) {
    ++ freq;
    int ils = 0; splitASdrug ( l, ils );
    int aim = ils; splitASdrug ( r, ils );
    for ( int i = 1; i <= aim; ++ i ) {
        for ( int j = aim + 1; j <= ils; ++ j )    transASsub ( itls[i].l, itls[i].r, itls[j].l, itls[j].r, freq );
    }
}

void add1 ( const int x ) { cur += buc2[col[x]], buc1[col[x]] ++; }
void add2 ( const int x ) { cur += buc1[col[x]], buc2[col[x]] ++; }
void sub1 ( const int x ) { cur -= buc2[col[x]], buc1[col[x]] --; }
void sub2 ( const int x ) { cur -= buc1[col[x]], buc2[col[x]] --; }
void captainMO () {
    int nowl = 0, nowr = 0;
    for ( int i = 1; i <= m; ++ i ) {
        for ( ; nowl < asq[i].l; add1 ( ++ nowl ) ) ;
        for ( ; nowr < asq[i].r; add2 ( ++ nowr ) ) ;
        for ( ; nowl > asq[i].l; sub1 ( nowl -- ) ) ;
        for ( ; nowr > asq[i].r; sub2 ( nowr -- ) ) ;
        ans[asq[i].ID] += cur * asq[i].x;
    }
}

int main () {
    n = rint (); int _waste_ = rint ();
    for ( int i = 1; i <= n; ++ i )    a[i] = pri[i] = rint ();
    for ( int i = 1; i < n; ++ i ) {
        int u = rint (), v = rint ();
        addE ( u, v ), addE ( v, u );
    }
    initial (), rt = 1;
    for ( int i = 1; i <= _waste_; ++ i ) {
        int c = rint (), x, y;
        if ( c == 1 )    rt = rint ();
        else    x = rint (), y = rint (), transASmany ( x, y );
    }
    sort ( asq + 1, asq + 1 + m, existcmp ), captainMO ();
    for ( int i = 1; i <= freq; ++ i )    wint ( ans[i] ), putchar ( '\n' );
    return 0;
}

Prob. 1

Desc. & Link.

行走的形式是比较自由的,因为只要走到了最优答案处就可以不管了,所以不需要考虑游戏的结束。

考虑二分答案。

然后预处理出每个节点到 $s$(另一棵树就是 $t$)的距离。

判断答案合法性:

首先枚举 $A$ 树的每个节点(节点编号小于当前二分的答案),然后判断需要构成答案的 $B$ 树上的节点距离 $t$ 的距离的奇偶性是否一致即可。

但是这样有一个问题:我们如何确保这个答案是整个一轮移动过程中最大的?

仔细考虑一下,我们判断成功的依据是行走过程中所有和不超过我们当前二分的值,那么转为判断这个问题(意思就是前面降智了)。

因为这是一棵树,所以该走的路径一定会走。

因为我们枚举了 $A$ 树中的节点,所以每次从哪两个节点走到 $s$、$t$ 是固定下来的。

草,直接 bfs 判断找可达最小值就行了。

$\Theta(n\log_{2}^{2}n)$,我觉得不行,先写。

草卡卡常过了。

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize(4)
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")
#include <cstdio>
#include <queue>

using namespace std;

const int MAXN = 1e6 + 5;

namespace IO{
    const int sz=1<<22;
    char a[sz+5],b[sz+5],*p1=a,*p2=a,*t=b,p[105];
    inline char gc(){
        return p1==p2?(p2=(p1=a)+fread(a,1,sz,stdin),p1==p2?EOF:*p1++):*p1++;
    }
    template<class T> void gi(T& x){
        x=0; char c=gc();
        for(;c<'0'||c>'9';c=gc());
        for(;c>='0'&&c<='9';c=gc())
            x=x*10+(c-'0');
    }
    inline void flush(){fwrite(b,1,t-b,stdout),t=b; }
    inline void pc(char x){*t++=x; if(t-b==sz) flush(); }
    template<class T> void pi(T x,char c='\n'){
        if(x==0) pc('0'); int t=0;
        for(;x;x/=10) p[++t]=x%10+'0';
        for(;t;--t) pc(p[t]); pc(c);
    }
    struct F{~F(){flush();}}f; 
}
using IO::gi;
using IO::pi;

template<typename _T> _T MIN ( const _T x, const _T y ) { return x < y ? x : y; }

struct GraphSet {
    int to, nx;
    GraphSet ( int T = 0, int N = 0 ) { to = T, nx = N; }
} asA[MAXN * 2], asB[MAXN * 2];

int n, s, t, cntA, cntB, beginA[MAXN], beginB[MAXN], disA[MAXN], disB[MAXN], visA[MAXN], visB[MAXN];

void makeEdgeA ( const int u, const int v ) { asA[++ cntA] = GraphSet ( v, beginA[u] ), beginA[u] = cntA; }
void makeEdgeB ( const int u, const int v ) { asB[++ cntB] = GraphSet ( v, beginB[u] ), beginB[u] = cntB; }

void dfsA ( const int u, const int lst, const int dis ) {
    disA[u] = dis;
    for ( int i = beginA[u]; i; i = asA[i].nx ) {
        int v = asA[i].to;
        if ( v == lst )    continue;
        dfsA ( v, u, dis + 1 );
    }
}

void dfsB ( const int u, const int lst, const int dis ) {
    disB[u] = dis;
    for ( int i = beginB[u]; i; i = asB[i].nx ) {
        int v = asB[i].to;
        if ( v == lst )    continue;
        dfsB ( v, u, dis + 1 );
    }
}

void behaveOneSide ( int ark, int& mnA, int& mnB, int& ord, priority_queue<int, vector<int>, greater<int>>& align ) {
    int preSave = mnA;
    while ( ! align.empty () ) {
        int u = align.top ();
        if ( u + mnB > ark )    break;
        else    align.pop ();
        for ( int i = beginA[u]; i; i = asA[i].nx ) {
            int v = asA[i].to;
            if ( visA[v] )    continue;
            visA[v] = 1, align.push ( v );
            mnA = MIN ( mnA, v );
        }
    }
    if ( mnA == preSave )    ++ ord;
    else    ord = 0;
}

void behaveAnotherSide ( int ark, int& mnA, int& mnB, int& ord, priority_queue<int, vector<int>, greater<int>>& align ) {
    int preSave = mnB;
    while ( ! align.empty () ) {
        int u = align.top ();
        if ( u + mnA > ark )    break;
        else    align.pop ();
        for ( int i = beginB[u]; i; i = asB[i].nx ) {
            int v = asB[i].to;
            if ( visB[v] )    continue;
            visB[v] = 1, align.push ( v );
            mnB = MIN ( mnB, v );
        }
    }
    if ( mnB == preSave )    ++ ord;
    else    ord = 0;
}

priority_queue<int, vector<int>, greater<int>> oneQ, anotherQ;
bool check ( const int x ) {
    for ( int i = 1; i <= n; ++ i )    visA[i] = visB[i] = 0;
    for ( ; ! oneQ.empty (); oneQ.pop () ) ;
    for ( ; ! anotherQ.empty (); anotherQ.pop () ) ;
    oneQ.push ( s ), anotherQ.push ( t );
    visA[s] = 1, visB[t] = 1;
    int turn = 0, mnA = s, mnB = t, ord = 0;
    while ( mnA > 1 || mnB > 1 ) {
        turn ^= 1;
        if ( turn )    behaveOneSide ( x, mnA, mnB, ord, oneQ );
        else    behaveAnotherSide ( x, mnA, mnB, ord, anotherQ );
        if ( ord > 2 )    break;
    }
    if ( mnA == 1 && mnB == 1 )    return 1;
    else    return 0;
}

int solve ( int l, int r ) {
    while ( l + 1 < r ) {
        int mid = ( l + r ) >> 1;
        if ( check ( mid ) )    r = mid;
        else    l = mid;
    }
    return r;
}

int main () {
    int tCase;
    gi ( tCase );
    while ( tCase -- > 0 ) {
        gi ( n ), cntA = cntB = 0;
        for ( int i = 1; i <= n; ++ i )    beginA[i] = 0, beginB[i] = 0;
        for ( int i = 1, u, v; i < n; ++ i ) {
            gi ( u ), gi ( v );
            makeEdgeA ( u, v ), makeEdgeA ( v, u );
        }
        for ( int i = 1, u, v; i < n; ++ i ) {
            gi ( u ), gi ( v );
            makeEdgeB ( u, v ), makeEdgeB ( v, u );
        }
        gi ( s ), gi ( t );
        // dfsA ( s, 0, 0 ), dfsB ( t, 0, 0 );
        pi ( solve ( 1, n << 1 ) );
    }
    return 0;
}

Prob. 2

Desc. & Link.

$n$ 很小,$q$ 也很小。

感觉这个 $n$ 不是 $2^{n}$ 的算法也不是多项式算法欸。

但复杂度一定与 $n$ 有关……

草这玩意儿折半是不是可以折半搜索?

我们可以搜出两边我们可以凑出的价格,分别记为 $A_{i},i\in[1,C_{A}]$、$B_{i},i\in[1,C_{B}]$。

然后让 $A,B$ sorted。

然后枚举 $A_{i}$,找到 $B$ 中最大的能与 $A_{i}$ 相加小于等于需要的值,然后算下贡献即可(bullshit)。

不是为什么用快读本地过数据提交瓦爆啊。。。

#include<cstdio>
#include<algorithm>
using namespace std;
void read(long long &hhh)
{
    long long x=0;
    char c=getchar();
    while(((c<'0')|(c>'9'))&(c^'-'))    c=getchar();
    if(c^'-')    x=c^'0';
    char d=getchar();
    while((d>='0')&(d<='9'))
    {
        x=(x<<3)+(x<<1)+(d^'0');
        d=getchar();
    }
    if(c^'-')    hhh=x;
    else    hhh=-x;
}
void writing(long long x)
{
    if(!x)    return;
    if(x>9)    writing(x/10);
    putchar((x%10)^'0');
}
void write(long long x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    else if(!x)
    {
        putchar('0');
        putchar('\n');
        return;
    }
    writing(x);
    putchar('\n');
}
long long n,q,endone,beginano,onesiz,onebuc[2000005],anosiz,anobuc[2000005],opl,opr,cud[45];
void dfs(long long now,long long cur)
{
    if(now==endone+1)    onebuc[++onesiz]=cur;
    else
    {
        dfs(now+1,cur+cud[now]);
        dfs(now+1,cur);
    }
}
void exdfs(long long now,long long cur)
{
    if(now==n+1)    anobuc[++anosiz]=cur;
    else
    {
        exdfs(now+1,cur+cud[now]);
        exdfs(now+1,cur);
    }
}
long long solve(long long mos)
{
    long long now=anosiz;
    long long res=0;
    for(long long i=1;i<=onesiz;++i)
    {
        while(now&&onebuc[i]+anobuc[now]>mos)    now--;
        res+=now;
    }
    return res;
}
int main()
{
//    read(n);
//    read(q);
    scanf("%lld%lld",&n,&q);
//    for(long long i=1;i<=n;++i)    read(cud[i]);
    for(long long i=1;i<=n;++i)    scanf("%lld",&cud[i]);
    endone=(n>>1);
    beginano=endone+1;
    dfs(1,0);
    exdfs(beginano,0);
    sort(onebuc+1,onebuc+onesiz+1);
    sort(anobuc+1,anobuc+anosiz+1);
    while(q--)
    {
        scanf("%lld%lld",&opl,&opr);
//        read(opl);
//        read(opr);
//        write(solve(opr)-solve(opl-1));
        printf("%lld\n",solve(opr)-solve(opl-1));
    }
    return 0;
}

Prob. 4

Desc. & Link.

相当于在树上对于每一个点找出找出一条以其为链顶的链。

设 $f_{u}$ 为 $u$ 的答案。

$$ f_{u}=\max_{v\in\text{son}(u)}\{f_{v}+(a_{u}-\text{dis}(u,v))\times b_{v},0\} $$

有乘法,然后题目中一堆常数。

:-) 斜率优化

我们令 $s_{u}=\text{dis}(1,u)$,然后

$$ \begin{aligned} f_{u} &=\max_{v\in\text{son}(u)}\{f_{v}+(a_{u}+s_{u}-s_{v})\times b_{v},0\} \\ &=\max_{v\in\text{son}(u)}\{(a_{u}-s_{u})\times b_{v}+f_{v}-s_{v}\times b_{v},0\} \end{aligned} $$

令 $y=f_{u},x=a_{u}-s_{u},k=b_{v},b=f_{v}-s_{v}\times b_{v}$,那么这个东西就是一个 $y=kx+b$。

那么我们现在需要在子树里维护凸包,并且能够支持合并凸包和插入直线。

Problem. 1 Junior - Thinking

Desc. & Link.

注意到值域乘范围刚好能过。

然后就存两个桶即可。。。(数组开小飞了半天才调出来。。。)

Problem. 2 Junior / Senior - Thinking

Desc. & Link.

考虑一次反转后对整个序列造成的影响。

每次操作相当于把整个序列分成了 $2^{n-q}$ 个块,我们只需要考虑块内和块外。

考虑一个块对其他块的情况。

嗯。

没有影响,完。

那么我们只需要考虑如何快速计算出每个块内的变化即可。

像归并排序一样考虑问题,把序列分成 $n$ 层(把二叉树画出来)。

要反转区间 $[l,r]$ 就要翻转 $[l,m],[m+1,r],m=\lfloor\frac{l+r}{2}\rfloor$,以此类推。

然后就预处理出每一层顺序对逆序对即可。

Problem. 3 Junior / Senior - Thinking

首先否决往大了想。

我们首先可以通过背包算出所有能够组成的值。

然后不会了。

Description

Link.

区间众数出现次数强制在线。

Solution

三个 YLST 中比较清新的一个分块。

比较重点的地方在于询问散块的处理。

先离散化一下序列。

我们首先预处理出来一个 vector 数组 fur[i]fur[i] 里面依次存的是所有 isa[i](即这个序列,详见代码)的出现位置,再预处理一个 pos[i] 表示在当前第 $i$ 位时 fur[i] 的大小也就是一共出现了多少个 isa[i]。由于 vector 的下标是从 $0$ 开始的,所以所有的 pos[i] 都需要减个一。

然后询问处理整块的时候,我们先假设当前询问的区间是 [opl,opr],然后把当前询问的答案 res 先置为 App[bel[opl] + 1][bel[opr] - 1]

然后来考虑散块,在处理出的 vector 数组中判断即可。

设散块处理到数 isa[i],那么如果存在 pos[i] + res <= fur[isa[i]].size() - 1fur[isa[i]][pos[i] + res] <= opr,那么则说明这个数出现了至少 res + 1 次,将 res 加一即可。

由于 res 最多加不超过 $\Theta(2\sqrt{n})$ 次,所以复杂度是对的。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>

using namespace std;

const int MAXN = 5e5 + 5, MAXM = 720 + 5;

char buf[1 << 21], *p1 = buf, *p2 = buf;
#define getchar( ) ( p1 == p2 && ( p2 = ( p1 = buf ) + fread( buf, 1, 1 << 21, stdin ), p1 == p2 ) ? EOF : *p1 ++ )

template<typename _T>
void read( _T &x ){
    x = 0; char c = getchar( ); _T f = 1;
    while( c < '0' || c > '9' ){ if( c == '-' )    f = -1; c = getchar( ); }
    while( c >= '0' && c <= '9' ){ x = ( x << 3 ) + ( x << 1 ) + ( c & 15 ); c = getchar( ); }
    x *= f;
}

template<typename _T>
void write( _T x ){
    if( x < 0 ){ putchar( '-' ); x = -x; }
    if( x > 9 ){ write( x / 10 ); }
    putchar( x % 10 + '0' );
}

template<typename _T>
void swapp( _T &one, _T &another ){ int temp = one; one = another; another = temp; }

template<typename _T>
_T MIN( _T one, _T another ){ return one > another ? another : one; }

template<typename _T>
_T MAX( _T one, _T another ){ return one > another ? one : another; }

int N, M;
int cube, each, kase, isa[MAXN], cnt[MAXN], pos[MAXN], vis[MAXN], bel[MAXN];
int lps[MAXM], rps[MAXM], app[MAXM], App[MAXM][MAXM];
vector<int> disc, fur[MAXN];

int getID( int x ){ return lower_bound( disc.begin( ), disc.end( ), x ) - disc.begin( ) + 1; }

void build( ){
    for( int i = 1; i <= cube; ++ i ){
        kase ++;
        for( int j = lps[i]; j <= rps[i]; ++ j ){
            if( vis[isa[j]] != kase )    cnt[isa[j]] = 0;
            cnt[isa[j]] ++; app[i] = MAX( app[i], cnt[isa[j]] );
            vis[isa[j]] = kase;
        }
    }
    memset( cnt, 0, sizeof( cnt ) );
    for( int i = 1; i <= cube; ++ i ){
        kase ++;
        for( int j = i; j <= cube; ++ j ){
            App[i][j] = App[i][j - 1];
            for( int k = lps[j]; k <= rps[j]; ++ k ){
                if( vis[isa[k]] != kase )    cnt[isa[k]] = 0;
                cnt[isa[k]] ++; App[i][j] = MAX( App[i][j], cnt[isa[k]] );
                vis[isa[k]] = kase;
            }
        }
    }
    memset( cnt, 0, sizeof( cnt ) );
}

int query( int opl, int opr ){
    if( bel[opl] == bel[opr] ){
        int res = 0; kase ++;
        for( int i = opl; i <= opr; ++ i ){
            if( vis[isa[i]] != kase )    cnt[isa[i]] = 0;
            cnt[isa[i]] ++; res = MAX( res, cnt[isa[i]] );
            vis[isa[i]] = kase;
        }
        return res;
    }
    int res = 0;
//    res = App[bel[opl] + 1][bel[opr] - 1];
    for( int i = bel[opl] + 1; i < bel[opr]; ++ i )    res += app[i];
//    for( int i = bel[opl] + 1; i < bel[opr]; ++ i )    res += App[i][i];
    for( int i = opl; i <= rps[bel[opl]]; ++ i ){
        int lim = fur[isa[i]].size( ) - 1;
        while( pos[i] + res <= lim && fur[isa[i]][pos[i] + res] <= opr )    res ++;
    }
    for( int i = lps[bel[opr]]; i <= opr; ++ i ){
        while( pos[i] - res >= 0 && fur[isa[i]][pos[i] - res] >= opl )    res ++;
    }
    return res;
}

signed main( ){
    read( N ); read( M ); each = 720; cube = ( N - 1 ) / each + 1;
    for( int i = 1; i <= N; ++ i ){ read( isa[i] ); disc.push_back( isa[i] ); }
    sort( disc.begin( ), disc.end( ) );
    disc.erase( unique( disc.begin( ), disc.end( ) ), disc.end( ) );
    for( int i = 1; i <= N; ++ i ){
        isa[i] = getID( isa[i] );
        fur[isa[i]].push_back( i );
        pos[i] = fur[isa[i]].size( ) - 1;
    }
    for( int i = 1; i <= cube; ++ i ){
        lps[i] = rps[i - 1] + 1; rps[i] = rps[i - 1] + each;
        if( i == cube )    rps[i] = N;
        for( int j = lps[i]; j <= rps[i]; ++ j )    bel[j] = i;
    }
    build( );
    int Ans = 0, opl, opr;
    while( M -- > 0 ){
        read( opl ); read( opr ); opl ^= Ans; opr ^= Ans;
        Ans = 0; if( opl > opr )    swapp( opl, opr );
        write( Ans = query( opl, opr ) ); putchar( '\n' );
    }
    return 0;
}