SWERC 2021-2022

A

题意:

\(n\)个物品,每个物品有个种类\(1\leq d_i\leq 10\)和价值\(b_i\),问是不是每种物品都出现过,如果出现过在每种物品种选一个使得价值和最大。

题解:

签到

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
    const int N=3e5+10,mod=998244353,inf=2e9;
    int n;
    int a[N];
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        int T;cin>>T;
        while(T--)
        {
            cin>>n;
            for(int i=1;i<=10;++i) a[i]=0;
            for(int i=1;i<=n;++i)
            {
                int x,y;cin>>x>>y;
                a[y]=max(a[y],x);
            }
            bool flag=0;
            int sum=0;
            for(int i=1;i<=10;++i)
            {
                if(a[i]==0) flag=1;
                sum+=a[i];
            }
            if(flag) cout<<"MOREPROBLEMS\n";
            else cout<<sum<<'\n';
        }
    }
}
signed main()
{
    red::main();
    return 0;
}
/*
1
5
1 2
2 3
3 4
3 5

*/

D

题意:

给定两个字符串\(a,b\),仅由\(A,B,C\)构成。

可以在\(a\)串中任意位置添加或删除\(AA、BB、CC、ABAB、BCBC\)

\(a\)串能不能变成\(b\)

\(|a,b|\leq 200,t\leq 100\)

题解:

考虑在\(a,b\)串中一起删除子串,看最后两个字符串是不是能变成一样。

首先,任何操作都不能改变字符串任意字符的奇偶性,所以如果两个字符串的奇偶性不同,那肯定不能变成。

其次,一个\(BA\)串可以通过:

\[BA\\ BA\ ABAB\\ B\ BAB\\ AB \]

翻转过来,类似的\(AB\)也可以翻转过来,\(BC\)\(CB\)也可以互相翻转。

所以可以看作\(AB、BC\)之间可以互相交换位置,而\(AC\)不行。

那可以都把\(B\)放到字符串的最后去,如果两个字符串中\(B\)中出现的奇偶性相同,则\(B\)对答案无影响,可以直接忽略。

剩下的就是\(A\)\(C\),他们之间的相对位置不能改变,所以剩下的部分的相对位置就决定两个字符串能不能互相转化。

对于剩下的,如果满足有连续两个相同的字母,可以直接删去,留下类似\(ACACAC…\)的串。

如果\(a\)\(b\)最后留下的串相同,就可以互相转化,否则不行。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
    const int N=3e5+10,mod=998244353,inf=2e9;
    int n,m,num1,num2;
    char a[N],b[N];
    bool dp[N];
    char s1[N],s2[N];
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        int T;cin>>T;
        while(T--)
        {
            cin>>(a+1)>>(b+1);
            n=strlen(a+1);
            m=strlen(b+1);
            num1=num2=0;
            int sum1=0,sum2=0;
            for(int i=1;i<=n;++i)
            {
                if(a[i]=='B') {sum1^=1;continue;}
                s1[++num1]=a[i];
                while(num1>1&&s1[num1]==s1[num1-1]) num1-=2;
            }
            for(int i=1;i<=m;++i)
            {
                if(b[i]=='B') {sum2^=1;continue;}
                s2[++num2]=b[i];
                while(num2>1&&s2[num2]==s2[num2-1]) num2-=2;
            }
            bool flag=1;
            if(num1!=num2||sum1!=sum2) flag=0;
            for(int i=1;i<=min(num1,num2);++i) if(s1[i]!=s2[i]) flag=0;
            if(flag) cout<<"YES\n";
            else cout<<"NO\n";
        }
    }
}
signed main()
{
    red::main();
    return 0;
}
/*


*/

F

题意:

给定一数列\(a\),如果\(|i-j|\leq min(a_i,a_j)\)则两个位置能花\(1s\)互相通信。

现在\(s\)想给\(t\)传消息,最少花多长时间?

\(n\leq 1e6,a_i\leq n\)

题解:

考虑\(bfs\),但是边数可能是\(n^2\)级别的,一个优化思路是根据\(bfs\)的特点,当进入到一个节点后,这个节点的最短距离已经定好了,我们希望把其他指向这个节点的边都去掉。

但是暴力去掉也是\(O(n)\),最后还是\(O(n^2)\)

对于一个位置\(i\),设\(l_i=max(1,i-a_i),r_i=min(n,i+a_i)\)

如果有\(j<i\)\(l_i\leq j,r_j\geq i\),那么\(i\)就可以向\(j\)传递消息。

这个可以用线段树来维护,每次找到\(r_j\)最大的位置。

每次找一个\(j\),直到\(r_j<i\)为止。

然后我们从\(i\)\(j\)传递消息之后,等于是在\(bfs\)中已经走到\(j\)了,怎么把\(j\)的入边全部删掉呢?

只要让\(l_j=inf,r_j=-inf\),这样任何其他节点都不会再向\(j\)传递消息了。因为不存在其他节点\(k\)满足\(r_j\geq k\)了。

复杂度\(O(nlog n)\)

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
    const int N=3e5+10,mod=998244353,inf=2e9;
    int n,st,ed;
    int a[N];
    int dp[N];
    typedef pair<int,int> pr;
    struct segment_tree1
    {
        int ans[N<<2],ret[N<<2];
        inline void build(int l,int r,int p)
        {
            ans[p]=inf;
            if(l==r)
            {
                ret[p]=l;
                return;
            }
            build(l,mid,ls(p));
            build(mid+1,r,rs(p));
        }
        inline void update(int pos,int l,int r,int p,int k)
        {
            if(l==r)
            {
                ans[p]=k;
                return;
            }
            if(pos<=mid) update(pos,l,mid,ls(p),k);
            if(pos>mid) update(pos,mid+1,r,rs(p),k);
            if(ans[ls(p)]<=ans[rs(p)]) ret[p]=ret[ls(p)],ans[p]=ans[ls(p)];
            else ret[p]=ret[rs(p)],ans[p]=ans[rs(p)];
        }
        inline pr query(int tl,int tr,int l,int r,int p)
        {
            if(tl<=l&&r<=tr)
            {
                return pr(ans[p],ret[p]);
            }
            if(tr<=mid) return query(tl,tr,l,mid,ls(p));
            if(tl>mid) return query(tl,tr,mid+1,r,rs(p));
            pr t1=query(tl,tr,l,mid,ls(p)),t2=query(tl,tr,mid+1,r,rs(p));
            if(t1.first<=t2.first) return t1;
            return t2;
        }
    }T1;
    struct segment_tree2
    {
        int ans[N<<2],ret[N<<2];
        inline void build(int l,int r,int p)
        {
            ans[p]=-inf;
            if(l==r)
            {
                ret[p]=l;
                return;
            }
            build(l,mid,ls(p));
            build(mid+1,r,rs(p));
        }
        inline void update(int pos,int l,int r,int p,int k)
        {
            if(l==r)
            {
                ans[p]=k;
                return;
            }
            if(pos<=mid) update(pos,l,mid,ls(p),k);
            if(pos>mid) update(pos,mid+1,r,rs(p),k);
            if(ans[ls(p)]>=ans[rs(p)]) ret[p]=ret[ls(p)],ans[p]=ans[ls(p)];
            else ret[p]=ret[rs(p)],ans[p]=ans[rs(p)];
        }
        inline pr query(int tl,int tr,int l,int r,int p)
        {
            if(tl<=l&&r<=tr)
            {
                return pr(ans[p],ret[p]);
            }
            if(tr<=mid) return query(tl,tr,l,mid,ls(p));
            if(tl>mid) return query(tl,tr,mid+1,r,rs(p));
            pr t1=query(tl,tr,l,mid,ls(p)),t2=query(tl,tr,mid+1,r,rs(p));
            if(t1.first>=t2.first) return t1;
            return t2;
        }
    }T2;
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        int T;cin>>T;
        while(T--)
        {
            cin>>n>>st>>ed;
            T1.build(1,n,1);
            T2.build(1,n,1);
            for(int i=1;i<=n;++i)
            {
                cin>>a[i];
                T1.update(i,1,n,1,i-a[i]);
                T2.update(i,1,n,1,i+a[i]);
                dp[i]=0;
            }
            queue<int> q;
            q.push(st);
            dp[st]=0;
            T1.update(st,1,n,1,inf);
            T2.update(st,1,n,1,-inf);
            while(!q.empty())
            {
                int i=q.front();q.pop();
                while("miao")
                {
                    pr tmp=T2.query(max(1ll,i-a[i]),i,1,n,1);
                    if(tmp.first<i) break;
                    int j=tmp.second;
                    dp[j]=dp[i]+1;
                    q.push(j);
                    T1.update(j,1,n,1,inf);
                    T2.update(j,1,n,1,-inf);
                }
                while("miao")
                {
                    pr tmp=T1.query(i,min(n,i+a[i]),1,n,1);
                    if(tmp.first>i) break;
                    int j=tmp.second;
                    dp[j]=dp[i]+1;
                    q.push(j);
                    T1.update(j,1,n,1,inf);
                    T2.update(j,1,n,1,-inf);
                }
            }
            cout<<dp[ed]<<'\n';
        }
    }
}
signed main()
{
    red::main();
    return 0;
}
/*
1
5
1 2
2 3
3 4
3 5

*/

还有思路\(2\)

考虑施加一个约束:每次只能从\(j\)走到\(i(j<i)\)

每次只要找满足要求的\(j\)\(j<i\)\(l_i\leq j,r_j\geq i\)

然后\(dp[i]=min\{dp[j]\}+1\)

我们可以把满足要求的\(j\)的线段树上的位置\(ans\)设为正确值,超过\(r_j\)表示范围之后设为\(inf\)

然后再反向跑一遍。

这样考虑有\(a<b<c<d\),我们从\(b->c->a->d\),那么一定有:

如果\(|c-b|=x\)

\(|b-a|>x\)

\(|d-c|>2x\)

image-20220426235556252

距离起点至少翻了\(3\)倍。

也就是最多\(log\)次就能得到所有点的最短路。

只要循环到所有点的最短距离都不再变化为止。

复杂度\(O(nlog^2n)\)

G

题意:

给一颗\(n\)个节点的树,把\(1\sim n\)\(n\)个数填入\(n\)个节点,让树上数字连续增大的路径条数最多。

题解:

先考虑一对相邻的节点\(a,b\),如果\(num_a<num_b\),则通过这对这节点的路径都是从\(a\)走向\(b\)的,反之都是从\(b\)走到\(a\)

那么问题能否变成给树上的每一条边确定一个方向,让树上不同的有向路径条数最多。

可以,考虑任意一种边的定向,它一定是\(DAG,\)我们根据它的拓扑排序可以确定填数的方案。

下面考虑最优化方案数:

如果存在一条长度大于等于\(4\)的链,满足

image-20220427002058576

那么翻转以\(a\)为根的子树和\(a->b\)的边,或者以\(c\)为根的子树和\(c->d\)的边会让答案更好。

证明:假如翻转了前者。

\(a\)的非\(b\)子树内部的路径数不变。

\(A_{in}\)是终点为\(a\)的路径数。

\(B_{in}\)是终点为\(b\)的路径数。

\(B_{out}\)是起点为\(b\)的路径数。

翻转后\(\Delta_1 =A(B_{in}-B_{out})\)

同理,如果对\(c\)操作,则\(\Delta_2=D(C_{in}-C_{out})\)

因为存在\(c->……->b\)这条路径,所以\(C_{out}>B_{out}\)

同理\(B_{in}>C_{in}\)

\((B_{in}-B_{out})+(C_{in}-C_{out})\geq 2\)

那么要么\(B_{in}>B_{out}\),要么\(C_{in}>C_{out}\)

所以\(\Delta_1\)\(\Delta_2\)中至少有一个\(>0\)

而现在,以任意一个节点为根的最优方案,都满足根的儿子们的子树中,最多只有一个儿子的子树中的一条边与其他边方向不同。

否则就可以通过上面的理论改变某条边的方向让路径数更多。

而且别的子树的方向必须与这棵子树的方向相反,否则又可以通过上面的理论翻转。

image-20220427003612357

这时不妨设这个冲突点为根,那么根的所有儿子的子树中的边都是同一方向。

当某个节点为根,子树中的边都是同一方向时,路径数是\(\sum_{i=1}^{n}str[i]\),即所有节点子树和,加上跨过根的路径数。

设向上的路径数是\(S\),向下的是\(T\),且\(S+T=n-1\),跨过根的路径数是\(S*T\)。那么应该尽量让二者相当。

如果这个根存在一个子树,节点数量大于等于\(\frac{n}{2}\),那么其他所有子树都应该和这颗子树的边方向相反。

否则,我们就要用一个背包来算了!

好在,如果不存在,那这个节点就是重心,一棵树最多有两个重心。

我们记录\(cnt[i]\)为大小为\(i\)的子树有多少个,然后用多重背包的优化技巧,配合\(bitset\),而且最多有根号种不同的大小。

配合二进制是\(O(\frac{n\sqrt{n}*logn}{64})\),单调队列是\(O(\frac{n\sqrt{n}}{64})\)

其中最优答案一定在重心处取到:

假如现在根是\(rt\)\(rt\)不是重心。

最大的子树大小是\(X\),设最大子树中边的方向远离根,其他子树种边的方向指向根。

向这颗子树挪一步后,最优方案下远离根的数量变为\(Y\)

那么因为挪动导致子树内部路径减少数是\(X-Y\)

而跨过根节点的路径增加\(Y*X\)

\[Y*X-X+Y=(Y-1)*X+Y\geq 0 \]

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
    const int N=1e6+10,mod=998244353,inf=2e9;
    int n,rt,ans;
    vector<int> eg[N];
    int str[N];
    int cnt[N];
    inline void dfs(int now,int fa)
    {
        str[now]=1;
        for(int t:eg[now])
        {
            if(t==fa) continue;
            dfs(t,now);
            str[now]+=str[t];
        }
    }
    int st[N],top;
    inline void find(int now,int fa)
    {
        bool flag=0;
        if((n-str[now])*2>n) flag=1;
        for(int t:eg[now])
        {
            if(t==fa) continue;
            if(str[t]*2>n) flag=1;
            find(t,now);
        }
        if(!flag) rt=now;
    }
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        cin>>n;
        for(int i=2;i<=n;++i)
        {
            int x;cin>>x;
            eg[x].emplace_back(i);
            eg[i].emplace_back(x);
        }
        dfs(1,0);
        find(1,0);
        dfs(rt,0);
        bitset<1000000> b;
        b[0]=1;
        for(int t:eg[rt])
        {
            ++cnt[str[t]];
        }
        for(int i=0;i<n;++i)
        {
            if(cnt[i]>0)
            {
                int k=1;
                while(k<=cnt[i])
                {
                    b|=b<<(i*k);
                    cnt[i]-=k;
                    k*=2;
                }
                if(cnt[i]>k)
                {
                    b|=b<<(cnt[i]*i);
                }
            }
        }
        for(int i=0;i<n;++i)
        {
            if(b[i]) ans=max(ans,i*(n-i-1));
        }
        ans+=accumulate(str+1,str+n+1,0ll);
        cout<<ans<<'\n';
    }
}
signed main()
{
    red::main();
    return 0;
}
/*


*/

H

队友会了就是我会了

I

题意:

\(n\)个住户,第\(i\)个住户里有\(a_i\)个人,位于\((i-1)*100\)

\(m\)个冰淇淋店,第\(i\)个店位于\(p_i\)

你想开一个店,如果一个住户离你比离别的店更近,住户就会来你这,你最多能吸引多少人?

题解:

对每个住户,找到离他最近的店,比这个距离短的范围内都可以把\(a_i\)个人吸引到手,做个差分。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
    const int N=2e6+10,mod=998244353,inf=2e9;
    int n,m,num;
    int a[N],s[N];
    int p[N];
    struct node
    {
        double pos;
        int val;
        inline bool operator < (const node &t) const
        {
            return pos<t.pos;
        }
    }c[N];
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        int T=1;
        while(T--)
        {
            int n,m;cin>>n>>m;
            for(int i=1;i<=n;++i)
            {
                cin>>a[i];
            }
            for(int i=1;i<=m;++i)
            {
                cin>>p[i];
            }
            sort(p+1,p+m+1);
            p[m+1]=inf;
            for(int i=1;i<=n;++i)
            {
                int pos=(i-1)*100;
                int t=lower_bound(p+1,p+m+1,pos)-p;
                double len=p[t]-pos;
                if(t>1) len=min(len,(double)(pos-p[t-1]));
                c[++num]=(node){pos-len+0.01,a[i]};
                c[++num]=(node){pos+len,-a[i]};
            }
            sort(c+1,c+num+1);

           //for(int i=1;i<=num;++i) cout<<c[i].pos<<' '<<c[i].val<<"!!"<<endl;
            int ans=0,sum=0;
            for(int i=1;i<=num;++i)
            {
                int j=i;
                while(c[j].pos==c[i].pos&&j<=num)
                {
                    sum+=c[j].val;
                    ++j;
                }
                --j;
                i=j;
                //cout<<c[i].pos<<"!!!"<<endl;
                //cout<<sum<<' '<<j<<"!!"<<endl;
                ans=max(ans,sum);
            }
            cout<<ans<<'\n';
        }
    }
}
signed main()
{
    red::main();
    return 0;
}
/*
3 3
5 10 12
300 50 50

*/

L

题意:

给定\(n\)个球,第\(i\)个球会在\(t_i\)秒出现在\(a_i\)位置,必须在\(t_i\)秒赶过去才能接住。

一开始第\(0\)秒你在\(0\)位置,最大速度是\(v\),问最多能接住多少球?

题解:

有朴素\(dp\)

\[dp[i]=max\{dp[j]+1\}[v*(t_i-t_j)\geq |a_i-a_j|] \]

考虑拆分绝对值符号

\(a_i\geq a_j\)

\[v*t_i-a_i\geq v*t_j-a_j\\ \]

\(a_i\leq a_j\)

\[v*t_i+a_i\geq v*t_j+a_j \]

\(x_i=v*t_i-a_i,y_i=v*t_i+a_i\),则必须满足\(x_j\leq x_i,y_j\leq y_i\)

把绝对值符号恢复,就等于是要求两个条件必须同时满足,也就是选一个序列出来,必须\(x\)\(y\)两个键值都不降。

\(x\)排序,求\(y\)的最长不下降子序列。

提前把从起点到不了的顶点去掉。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
    const int N=3e5+10,mod=998244353,inf=2e9;
    int n,m,num;
    int tim[N],pos[N];
    struct node
    {
        int x,y;
        inline bool operator < (const node &t) const
        {
            if(x!=t.x) return x<t.x;
            return y<t.y;
        }
    }a[N];
    int dp[N];
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        int T=1;
        while(T--)
        {
            cin>>n>>m;
            for(int i=1;i<=n;++i)
            {
                cin>>tim[i];
            }
            for(int i=1;i<=n;++i)
            {
                cin>>pos[i];
                if(abs(pos[i])>m*tim[i]) continue;
                ++num;
                a[num].x=tim[i]*m-pos[i];
                a[num].y=tim[i]*m+pos[i];
            }
            sort(a+1,a+num+1);
            for(int i=1;i<=num;++i) dp[i]=inf*inf;
            int ans=0;
            for(int i=1;i<=num;++i)
            {
                if(a[i].x<0) continue;
                int t=upper_bound(dp+1,dp+num+1,a[i].y)-dp;
                ans=max(ans,t);
                dp[t]=min(dp[t],a[i].y);
            }
            cout<<ans<<'\n';

        }
    }
}
signed main()
{
    red::main();
    return 0;
}
/*
1
5
1 2
2 3
3 4
3 5

*/

M

题意:

有一排\(n\)瓶酒,每瓶可能红酒也可能是蓝酒。

\(m\)个人,每个表示自己选择了某个子区间,喝到了\(a\)瓶红酒和\(b\)瓶蓝酒。

能否构造一个满足所有人要求的排列?

\(n,a,b\leq 100\)

题解:

找到\(a_i\)的最大值,\(b_i\)的最大值,如果加起来超过\(n\)就无解。

否则左边放\(a\)瓶红酒,右边放\(n-a\)瓶蓝酒。

然后它们每个人喝了多少酒让它们从中间分界处自己挑。

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
    const int N=3e5+10,mod=998244353,inf=2e9;
    int n,m;
    int a[N];
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        int T;cin>>T;
        while(T--)
        {
            cin>>n>>m;
            int t1=0,t2=0;
            for(int i=1;i<=m;++i)
            {
                int x,y;
                cin>>x>>y;
                t1=max(x,t1),t2=max(y,t2);
            }
            if(t1+t2>n) cout<<"IMPOSSIBLE\n";
            else
            {
                t1=n-t2;
                while(t1--) cout<<"R";
                while(t2--) cout<<"W";
                cout<<'\n';
            }
        }
    }
}
signed main()
{
    red::main();
    return 0;
}
/*
1
5
1 2
2 3
3 4
3 5

*/

N

题意:

\(1\sim n^2\)放在\(n*n\)的格子里,问有多少个矩形满足,四个顶点中较小的两个数字是一条边的两个顶点?

\(n\leq 1500\)

考虑从小到大加入数字。

image-20220427152359937

我们考虑一个合法的矩形在加入时的情形:

我们在加入第二大和第三大的数字时,必然有一条边已经加入数字了,而另一条边上的数字还没有。

而不合法的情况,因为先加入的是对角线上的数字,所以两条边上要么已经都有数字了,要么还都空着。

\(u_i\)是第\(i\)行有多少已经加入的数字了,\(v_i\)是第\(i\)列有多少已经加入的数字了

\[2*ans=\sum_{i=1}^{n}[u_i(n-1-v_i)+v_i(n-1-u_i)] \]

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define double long double
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
#define mid ((l+r)>>1)
#define eps (1e-15)
    const int N=3e5+10,mod=998244353,inf=2e9;
    int n,m;
    int a[1510];
    int b[1510];
    struct node
    {
        int x,y,v;
        inline bool operator < (const node &t) const
        {
            return v<t.v;
        }
    }q[1500*1500+10];
    inline void main()
    {
        ios::sync_with_stdio(false);
        cin.tie(0);cout.tie(0);
        cin>>n;
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=n;++j)
            {
                int x;cin>>x;
                q[++m]=(node){i,j,x};
            }
        }
        sort(q+1,q+m+1);
        int ans=0;
        for(int i=1;i<=m;++i)
        {
            ans+=a[q[i].x]*(n-1-b[q[i].y])+b[q[i].y]*(n-1-a[q[i].x]);
            ++a[q[i].x],++b[q[i].y];
        }
        cout<<ans/2<<'\n';
    }
}
signed main()
{
    red::main();
    return 0;
}
/*
1
5
1 2
2 3
3 4
3 5

*/

O

队友会了就是我会了

posted @ 2022-04-27 15:29  lovelyred  阅读(286)  评论(0编辑  收藏  举报