[XXII Open Cup. Grand Prix of Korea]

XXII Open Cup. Grand Prix of Korea

A Automatic Sprayer 2

简要题意

定义两点间的距离为曼哈顿距离,每个点的点值非负,以这个点值向外扩散。给出最终的权值和,反推每个点的点值。\(E\)为求和后的矩阵,\(A\)为点值矩阵,即

\[E_{i,j} = \sum_{x,y} A_{x,y} (|x-i|+|y-j|) \]

题解

观察矩阵\(E\)发现相邻两行,两列之间的差值是定值,考虑两列元素的差值的意义。当前在第\(i\)列,向右移动一个单位,\(E\)的改变量是前\(i\)列的\(A\)之和减去后\(n-i\)列的和。这样的话我们可以得到第\(i\)列的和。

\(C_i = \sum_{j=1}^n A_{j,i}\)表示第\(i\)列的和,\(Sc_{i,j}\)表示第\(i\)列到第\(j\)列的和。

\[E_{1,i+1} = E_{1,i} + Sc_{1,i-1} + C_i - Sc_{i+1,n} \\ E_{1,i} = E_{1,i-1} + Sc_{1,i-1} - C_i - Sc_{i+1,n} \\ 2C_i = E_{1,i+1} + E_{1,i-1} - 2E_{1,i} \\ C_i = \frac{E_{1,i+1} + E_{1,i-1} - 2E_{1,i}} {2} \]

同理我们可以得到行和。

特别的,第一行,第\(n\)行,第一列,第\(n\)列要特殊处理

\[R_1 + C_1 = \frac{\bigg(E_{n,n} - \sum_{x=2}^{n-1} (|x-n|R_x) - \sum_{y=2}^{n-1}(|y-n|C_y) \bigg)} {n-1} \\ R_1 + C_n = \frac{\bigg(E_{n,n} - \sum_{x=2}^{n-1} (|x-n|R_x) - \sum_{y=2}^{n-1}(|y-1|C_y) \bigg)} {n-1} \\ R_n + C_1 = \frac{\bigg(E_{n,n} - \sum_{x=2}^{n-1} (|x-1|R_x) - \sum_{y=2}^{n-1}(|y-n|C_y) \bigg)} {n-1} \\ R_1 + R_n + \sum_{x=2}^{n-1} R_x = C_1 + C_n + \sum_{y=2}^{n-1} C_y \]

至此我们得到了每一行每一列的和,我们考虑如何求出一组满足条件的解。一个小技巧就是令

for i \leftarrow 1 to n
	for j \leftarrow 1 to n
		A_{i,j} \leftarrow min(R_i,C_j)
		R_i \leftarrow R_i - A_{i,j}
		C_j \leftarrow C_j - A_{i,j}

这样就能找到一组合法解。

#include <bits/stdc++.h>
#define LL long long
#define pb push_back
#define mp make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define lc nd<<1
#define rc nd<<1|1
#define lowbit(x) (x&(-x))
#define pLL pair<LL,LL>

using namespace std;

const int mn=1006;
LL e[mn][mn],r[mn],c[mn],ans[mn][mn];
int n;

int main()
{
    int tests=1;//scanf("%d",&tests);
    while(tests--) {
        scanf("%d",&n);
        for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) scanf("%lld",&e[i][j]);
        for(int i=2;i<n;++i) c[i]=(e[1][i-1]+e[1][i+1]-2*e[1][i])/2;
        for(int i=2;i<n;++i) r[i]=(e[i-1][1]+e[i+1][1]-2*e[i][1])/2;
        LL s1=e[n][n];
        for(int i=2;i<n;++i) s1-=r[i]*abs(i-n)+c[i]*abs(i-n);
        s1/=(n-1);
        LL s2=e[n][1];
        for(int i=2;i<n;++i) s2-=r[i]*abs(i-n)+c[i]*abs(i-1);
        s2/=(n-1);
        LL s3=e[1][n];
        for(int i=2;i<n;++i) s3-=r[i]*abs(i-1)+c[i]*abs(i-n);
        s3/=(n-1);
        LL s4=0;
        for(int i=2;i<n;++i) s4+=c[i]-r[i];
        r[1]=(s1+s2+s4+s1-s3)/4;
        r[n]=r[1]+s3-s1;
        c[1]=s1-r[1];
        c[n]=s2-r[1];
        for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) {
            ans[i][j]=min(r[i],c[j]);
            r[i]-=ans[i][j],c[j]-=ans[i][j];
        }
        for(int i=1;i<=n;++i) {
            for(int j=1;j<=n;++j) printf("%lld ",ans[i][j]);
            puts("");
        }
    }
    return 0;
}

C Equivalent Pipelines

简要题意

树上两点\(u,v\)间路径\(\{a\}\)满足\(a_0=u,a_d=v\)\(a_i\)\(a_{i-1}\)之间有边相连,距离定义为\(\min{w_{a_i,a_{i-1}}}\)

定义两棵树同构当且仅当

\[\forall i,j, \ d_a(i,j) = d_b(i,j) \]

给出\(d\)棵树,对第\(i\)棵树,输出和它同构的树的最小的编号。

题解

考虑通过建树过程判断同构。对边权从大到小排序,这样每加入一条边,会把两个子树合并起来,并且对于左右端点分别在两个子树内的\(d(i,j)\),它们的答案一定是当前边的边权。这样可以通过并查集和map记录所有信息。

判断记录下的信息相同时可以用哈希。

\[hash = \sum f_a*f_b*d(a,b) \]

哈希时一个小trick是给每个点随机权值,而不是用\(p^i\)表示\(i\)的权值。

#include <bits/stdc++.h>
#define LL long long
#define pb push_back
#define mp make_pair
#define pii pair<int,int>
#define fi first
#define se second
#define lc nd<<1
#define rc nd<<1|1
#define lowbit(x) (x&(-x))
#define pLL pair<LL,LL>

using namespace std;

const int mn=5e5+6,p=935935,mod1=1e9+7,mod2=998244353;
int d,n,fa[mn];
LL po1[mn],po2[mn],siz1[mn],siz2[mn];
map<LL,int> ans;

LL qpow(LL a,LL b,LL mod)
{
    LL ans=1;a%=mod;
    while(b) {
        if(b&1) ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}

struct edge {
    int u,v,w;
};
vector<edge> e;
bool cmp(edge a,edge b) {return a.w>b.w;}

vector<pair<pLL,LL>> vec1,vec2;

int find(int x) {if(fa[x]==x) return x;return fa[x]=find(fa[x]);}

void init()
{
    srand(998244353);
    for(int i=1;i<mn;++i) po1[i]=1ll*rand()*rand()%mod1,po2[i]=1ll*rand()*rand()%mod2;
}

int main()
{
    // freopen("t.in","r",stdin);
    init();
    scanf("%d%d",&d,&n);
    for(int _=1;_<=d;++_) {
        e.clear();vec1.clear();vec2.clear();
        int u,v,w;
        for(int i=1;i<=n;++i) fa[i]=i,siz1[i]=po1[i],siz2[i]=po2[i];
        for(int i=1;i<n;++i) {
            scanf("%d%d%d",&u,&v,&w);
            e.pb(edge{u,v,w});
        }
        sort(e.begin(),e.end(),cmp);
        for(int i=0;i<n-1;++i) {
            int x=find(e[i].u),y=find(e[i].v);
            if(x==y) continue;
            fa[x]=y;
            vec1.pb(mp(mp(siz1[x],siz1[y]),e[i].w));
            vec2.pb(mp(mp(siz2[x],siz2[y]),e[i].w));
            siz1[y]=(siz1[y]+siz1[x])%mod1;
            siz2[y]=(siz2[y]+siz2[x])%mod2;
        }
        LL s1=0,s2=0,val1,val2;
        for(int i=0;i<vec1.size();++i) {
            val1=vec1[i].fi.fi*vec1[i].fi.se%mod1;
            val2=vec2[i].fi.fi*vec2[i].fi.se%mod2;
            s1+=val1*vec1[i].se%mod1;
            s2+=val2*vec1[i].se%mod2;
        }
        s1%=mod1,s2%=mod2;
        if(ans.find(s1*mod2+s2*mod1)==ans.end()) {
            ans[s1*mod2+s2*mod1]=_;
        }
        printf("%d ",ans[s1*mod2+s2*mod1]);
    }
    return 0;
}
posted @ 2021-11-05 14:08  tianyy  阅读(314)  评论(0)    收藏  举报