Codeforces Round #264 (Div. 2)(好)

50 篇文章 0 订阅
40 篇文章 0 订阅

C. Gargari and Bishops

Gargari is jealous that his friend Caisa won the game from the previous problem. He wants to prove that he is a genius.

He has a n × n chessboard. Each cell of the chessboard has a number written on it. Gargari wants to place two bishops on the chessboard in such a way that there is no cell that is attacked by both of them. Consider a cell with number x written on it, if this cell is attacked by one of the bishops Gargari will get x dollars for it. Tell Gargari, how to place bishops on the chessboard to get maximum amount of money.

We assume a cell is attacked by a bishop, if the cell is located on the same diagonal with the bishop (the cell, where the bishop is, also considered attacked by it).

Input

The first line contains a single integer n (2 ≤ n ≤ 2000). Each of the next n lines contains n integers aij (0 ≤ aij ≤ 109) — description of the chessboard.

Output

On the first line print the maximal number of dollars Gargari will get. On the next line print four integers: x1, y1, x2, y2 (1 ≤ x1, y1, x2, y2 ≤ n), where xi is the number of the row where the i-th bishop should be placed, yi is the number of the column where the i-th bishop should be placed. Consider rows are numbered from 1 to n from top to bottom, and columns are numbered from 1 to n from left to right.

If there are several optimal solutions, you can print any of them.

Sample test(s)
Input
4
1 1 1 1
2 1 1 0
1 1 1 0
1 0 0 1
Output
12
2 2 3 2

思路:首先与处理处所有的对角线的元素和,然后暴力就行了,因为要求两个像不能有重叠,所以只需要枚举奇偶不同的就行了

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int maxn=2020;
typedef long long LL;
LL sum[2][maxn*2];
pair<int,int> ans[2];
int a[maxn][maxn],n;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)scanf("%d",&a[i][j]);
    memset(sum,0,sizeof(sum));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)sum[0][i-j+n]+=a[i][j],sum[1][i+j]+=a[i][j];
    LL first=0,two=0;
    ans[0]=make_pair(1,1),ans[1]=make_pair(1,2);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            LL tmp=sum[0][i-j+n]+sum[1][i+j]-a[i][j];
            if((i+j)&1)
            {
                if(tmp>first)
                    ans[0]=make_pair(i,j),first=tmp;
            }
            else if(tmp>two)
                ans[1]=make_pair(i,j),two=tmp;
        }
    }
    cout<<first+two<<endl;
    cout<<ans[0].first<<" "<<ans[0].second<<" "<<ans[1].first<<" "<<ans[1].second<<endl;
    return 0;
}

D. Gargari and Permutations

Gargari got bored to play with the bishops and now, after solving the problem about them, he is trying to do math homework. In a math book he have found k permutations. Each of them consists of numbers 1, 2, ..., n in some order. Now he should find the length of the longest common subsequence of these permutations. Can you help Gargari?

You can read about longest common subsequence there: https://en.wikipedia.org/wiki/Longest_common_subsequence_problem

Input

The first line contains two integers n and k (1 ≤ n ≤ 1000; 2 ≤ k ≤ 5). Each of the next k lines contains integers 1, 2, ..., n in some order — description of the current permutation.

Output

Print the length of the longest common subsequence.

Sample test(s)
Input
4 3
1 4 2 3
4 1 2 3
1 2 4 3
Output
3
Note

The answer for the first test sample is subsequence [1, 2, 3].

题意:求k个长度为n的序列的最长公共子序列

思路:以前在训练指南上做过一个类似的,是吧LCS转换成LIS,但那个是两个序列,这里是k个,首先根据第一个序列对2~k重新编号,然后根据第二个对3~k重新编号,以此类推,最后就是求最后一个序列的最长上升子序列,要注意这里要保证1~k-1也是上升的

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int maxn=1010;
const int INF=1000000000;
int num[maxn],g[maxn],d[maxn];
int a[6][maxn];
int n,k;
bool check(int x,int y)
{
    for(int i=k;i>1;i--)
        if(a[i][x]>a[i][y])
        {
            x=a[i][x];
            y=a[i][y];
        }
    else return false;
    return true;
}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=k;i++)
        for(int j=1;j<=n;j++)scanf("%d",&a[i][j]);
    for(int i=1;i<k;i++)
    {
        for(int j=1;j<=n;j++)num[a[i][j]]=j;
        for(int j=i+1;j<=k;j++)
            for(int p=1;p<=n;p++)a[j][p]=num[a[j][p]];
    }
    for(int i=1;i<=n;i++)g[i]=INF;
    int ans=0;
    for(int i=1;i<=n;i++)
        for(int j=0;j<i;j++)
        if(check(i,j)||j==0)
        d[i]=max(d[i],d[j]+1);
    for(int i=1;i<=n;i++)ans=max(ans,d[i]);
    cout<<ans<<endl;
    return 0;
}

E. Caisa and Tree

Caisa is now at home and his son has a simple task for him.

Given a rooted tree with n vertices, numbered from 1 to n (vertex 1 is the root). Each vertex of the tree has a value. You should answer q queries. Each query is one of the following:

  • Format of the query is "1 v". Let's write out the sequence of vertices along the path from the root to vertex v: u1, u2, ..., uk (u1 = 1; uk = v). You need to output such a vertex ui that gcd(value of ui, value of v) > 1 and i < k. If there are several possible vertices ui pick the one with maximum value of i. If there is no such vertex output -1.
  • Format of the query is "2 v w". You must change the value of vertex v to w.

You are given all the queries, help Caisa to solve the problem.

Input

The first line contains two space-separated integers n, q (1 ≤ n, q ≤ 105).

The second line contains n integers a1, a2, ..., an (1 ≤ ai ≤ 2·106), where ai represent the value of node i.

Each of the next n - 1 lines contains two integers xi and yi (1 ≤ xi, yi ≤ nxi ≠ yi), denoting the edge of the tree between vertices xi and yi.

Each of the next q lines contains a query in the format that is given above. For each query the following inequalities hold: 1 ≤ v ≤ n and 1 ≤ w ≤ 2·106. Note that: there are no more than 50 queries that changes the value of a vertex.

Output

For each query of the first type output the result of the query.

Sample test(s)
Input
4 6
10 8 4 3
1 2
2 3
3 4
1 1
1 2
1 3
1 4
2 1 9
1 4
Output
-1
1
2
-1
1
Note

gcd(x, y) is greatest common divisor of two integers x and y.

思路:暴力求出每个数值的质因子,dfs,每个素数开一个栈,存含有该质因数的结点,dfs和栈刚好契合,对于当前的点,它的质因数的栈如果不为空,那么栈顶就是离它最近也就是深度最大的符合要求的祖先,取一个深度最大的即可。然后把它push到栈里,继续dfs子节点,结束后pop掉即可。一直tle是拙计在了每次dfs到一个点就重新算一遍质因数,实际上读入时就可以分解质因数,存到vector里,当有修改操作时再单独分解那一个点,然后重新dfs。

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<algorithm>
using namespace std;
const int maxm=2000010;
const int maxn=100010;
int prime[2010],vis[2010];
int val[maxn],l[maxn],r[maxn],ans[maxn],dep[maxn];
int n,q,num,cnt;
vector<int> h[maxm];
vector<int> g[maxn];
void get_prime()
{
    num=0;
    vis[0]=vis[1]=1;
    for(int i=2;i<2001;i++)
    {
        if(!vis[i])
        {
            prime[num++]=i;
        }
        for(int j=0;j<num;j++)
        {
            if(i*prime[j]>=2001)break;
            vis[i*prime[j]]=1;
            if(i%prime[j]==0)break;
        }
    }
}
void add(int x,int y)
{
    h[x].push_back(y);
}
int find(int x)
{
    if(h[x].size())return h[x][h[x].size()-1];
    return 0;
}
void del(int x)
{
    if(h[x].size())h[x].pop_back();
}
void dfs(int u,int pos,int all,int flag,int fa)
{
    ans[u]=0;
    int tmp=val[u];
    for(int i=0;i<num&&prime[i]*prime[i]<=tmp;i++)
    {
        if(tmp%prime[i]==0)
        {
            int v=find(prime[i]);
            if(dep[ans[u]]<dep[v])ans[u]=v;
            add(prime[i],u);
            while(tmp%prime[i]==0)tmp/=prime[i];
        }
    }
    if(tmp>1)
    {
        int v=find(tmp);
        if(dep[ans[u]]<dep[v])ans[u]=v;
        add(tmp,u);
    }

    if(!flag)l[u]=++cnt;

    for(int i=0;i<g[u].size();i++)
    {
        int v=g[u][i];
        if(v==fa)continue;
        if(!flag)dep[v]=dep[u]+1;
        //if(all||flag==u||l[v]<=pos&&pos<=r[v])加上这句话跑400ms,不加跑3000ms,但是没太懂什么意思
        dfs(v,pos,all||flag==u,flag,u);
    }
    if(!flag)r[u]=cnt;
    tmp=val[u];
    for(int i=0;i<num&&prime[i]*prime[i]<=tmp;i++)
    {
        if(tmp%prime[i]==0)
        {
            del(prime[i]);
            while(tmp%prime[i]==0)tmp/=prime[i];
        }
    }
    if(tmp>1)
        del(tmp);
}
int main()
{
    get_prime();
    dep[1]=1;
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)scanf("%d",&val[i]);
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        g[x].push_back(y);
        g[y].push_back(x);
    }
    dfs(1,1,1,0,1);
    while(q--)
    {
        int op,u,v;
        scanf("%d",&op);
        if(op==1)
        {
            scanf("%d",&v);
            cout<<(ans[v]?ans[v]:-1)<<endl;
        }
        else
        {
            scanf("%d%d",&u,&v);
            val[u]=v;
            dfs(1,l[u],0,u,1);
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值