Codeforces Round #586 (Div. 1 + Div. 2) A,B,C,D,E(tarjan+思维)

题目链接:https://codeforces.com/contest/1220

A水题

B. Multiplication Table

这题我写复杂了,直接m[1][2]*m[1][3]/m[2][3]=a[1]*a[1]就可以了。

我求第一行的gcd,然后枚举这个gcd的因子去了。。。

#include
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
#define pi pair
#define mk make_pair
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
const int N=1e3+10;
ll a[N][N];
ll ans[N];
int n;
void cal(ll t)
{
    ans[1]=t;
    for(int i=2;i<=n;++i)ans[i]=a[1][i]/t;
        bool f=1;
    for(int i=1;i<=n&&f;++i)
    {
        for(int j=1;j<=n&&f;++j)
        {
            if(i==j) continue;
            else {
                if(ans[i]*ans[j]!=a[i][j]) f=0;
            }
 
        }
    }
    if(f)
    {
        for(int i=1;i<=n;++i) printf("%lld ",ans[i]);
       exit(0);
    }
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=n;++j)scanf("%lld",&a[i][j]);
    }
 
    ll gc=a[1][1];
    for(int j=2;j<=n;++j)
    {
        gc=gcd(gc,a[1][j]);
    }
    for(int k=1;k*k<=gc;++k)
    {
        if(gc%k==0)
        {
            cal(k);
            cal(gc/k);
        }
    }
 
    //for(int i=1;i<=n;++i) printf("%lld ",ans[i]);
 
}

C. Substring Game in the Lesson

水题。。

#include
#define rep(i,a,b) for(int i=a;i<=(b);++i)
#define mem(a,x) memset(a,x,sizeof(a))
#define pb push_back
#define pi pair
#define mk make_pair
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}
const int N=5e5+10;
char s[N];
int a[N],mi[N];
int main()
{
    cin>>s+1;
    int n=strlen(s+1);
    mi[0]=0x3f3f3f3f;
    for(int i=1;i<=n;++i)
    {
        a[i]=s[i]-'a';
        mi[i]=min(mi[i-1],a[i]);
    }
    for(int i=1;i<=n;++i)
    {
        if(mi[i-1]

D. Alex and Julian

题意:给你n个值,任意两个点只要满足|i-j|那么i和j就可以连一条边。。问如何去掉最少的值使得这个图是二分图?

判定二分图的基本方法:不存在奇环。但是可以得知如果有一个差值,d,那么2*d4*d,6*d....(偶数倍)是不能共存的。

那么换种思路:假如1存在,那么能与1共存的有3,5,7,9,11

2存在,能与2共存的有,6,10,14,。。。

3 存在,能与3共存的有9,15,21....

简单观察得:二进制下,最后一个1的位置相同的是可以存在一起的(半蒙带猜)

#include
using namespace std;
const int N=2e5+10;
typedef long long ll;
ll a[N];
int num[70];
int n;
vectorG[70];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i) scanf("%lld",&a[i]);

    for(int i=1;i<=n;++i)
    {
        ll x=a[i];
        for(int j=0;j<=64;++j)
        {
            if((x>>j)&1) {
                G[j].push_back(x);
                num[j]++;break;
            }
        }
    }
    int ans=num[0];
    int id=0;
    for(int i=0;i<=64;++i)
    {
        if(num[i]>ans)
        {
            ans=num[i],id=i;
        }
    }

    printf("%d\n",n-ans);
    for(int i=0;i<=64;++i)
    {
        if(i==id) continue;
        for(ll v:G[i]) printf("%lld ",v);
    }

}

E. Tourism

题意:给n个点和m条边,给n个点的权值。

给你一个起点,s。现从s点出发,经历一些点,并加上对应的点的权值。

条件:同一边不能连续走两次。

做法:tarjan缩点。然后第一次dfs去dp,D从s走不经历度为1的强连通能得到的最大权值

第二次dfs就直接走,拿之前的最大权值走,直到走完所有图,求最大值即可。

挺思维的

#include
using namespace std;
typedef long long ll;
const int N=2e5+10;
int dfn[N],low[N],cnt,bcc[N],index,bccnum;
int sz[N];
ll val[N],dp[N];
int top=0;
int sta[N];
int n,m;
vectorG[N],g[N];
void tarjan(int root,int fa)
{
	dfn[root]=low[root]=++index;
	sta[++top]=root;
	for(int v:g[root])
	{
        if(v==fa) continue;
		if(!dfn[v])
		{
			tarjan(v,root);
			low[root]=min(low[root],low[v]);
		}
		else low[root]=min(low[root],dfn[v]);
	}
	if(low[root]==dfn[root])
	{
		bccnum++;
		int x;
		do
		{
		    x=sta[top--];
			bcc[x]=bccnum;
			sz[bccnum]++;
            dp[bccnum]+=val[x];
		}while(x!=root);
	}
}
void dfs(int u,int fa)
{
    for(int v:G[u])
    {
        if(v==fa) continue;
        dfs(v,u);
        if(sz[v]>1)
        {
            dp[u]+=dp[v];
            dp[v]=0;
            sz[u]+=sz[v];
        }
    }
}
ll ans=0;
ll d[N];
void dfs1(int u,int fa,ll v)
{
    d[u]=dp[u]+v;
    ans=max(ans,d[u]);
    for(int v:G[u])
    {
        if(v==fa) continue;
        dfs1(v,u,d[u]);
    }
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i) scanf("%lld",&val[i]);
	int u,v;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&u,&v);
		g[u].push_back(v);
		g[v].push_back(u);
	}

	int s;
	scanf("%d",&s);
	for(int i=1;i<=n;i++)
		if(!dfn[i]) tarjan(i,0);

    for(int i=1;i<=n;++i)
    for(int v:g[i])
    if(bcc[i]!=bcc[v])
    {
        G[bcc[i]].push_back(bcc[v]);//有向边?
        //G[bcc[v]].push_back(bcc[i]);
    }
    s=bcc[s];
    dfs(s,0);
    dfs1(s,0,0);
    printf("%lld\n",ans);
	return 0;
}

 

你可能感兴趣的:(codeforce题解)