2018SD省队集训R1 D1

T1

题解:

经过每条边至少一次并且回到原点,我们可以想到欧拉回路。发现欧拉回路的特点是每个点度数为偶数,然后我们的目标就是把度数奇数的那些点通过添加一些长度尽量小的边变成度数为偶数。
添加哪些边呢?不难想到是最小生成树上的边,那我们先添加成最小生成树,其实就是每条边可以选/不选,每个点的要求被选的相邻边是奇数/偶数。最小化权值,自底向上dfs一遍就好了

代码:

#include 
#include 
#define LL long long 
using namespace std;
const int N=500010;
int tot,nxt[N*2],point[N],fa[N],v[N*2],x[N],y[N],c[N*2],du[N];
const int mod=1e9+7;LL ans;
LL ksm(LL a,LL k)
{
    LL ans=1;
    for (;k;k>>=1,a=a*a%mod)
      if (k&1) ans=ans*a%mod;
    return ans;
}
void addline(int x,int y,int vv)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=vv;
    ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=vv;
}
bool dfs(int x,int fa)
{
    for (int i=point[x];i;i=nxt[i])
      if (v[i]!=fa)
      {
        bool have=dfs(v[i],x);
        if (have) ans=(ans+ksm(2,c[i]))%mod,du[x]++;
      }
    if (du[x]%2) {du[x]++;return 1;}
    else return 0;
}
int find(int x)
{
    if (fa[x]!=x) return fa[x]=find(fa[x]);
    return x;
}
int main()
{
    int n,m;scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++)
    {
        scanf("%d%d",&x[i],&y[i]);
        du[x[i]]++; du[y[i]]++;
    }
    for (int i=1;i<=n;i++) fa[i]=i;int id=0;
    for (int i=1;i<=m;i++)
    {
        int f1=find(x[i]),f2=find(y[i]);
        if (f1!=f2)
        {
            fa[f1]=f2;
            addline(x[i],y[i],i);
            id++; if (id==n-1) break;
        }
    }
    ans=(ksm(2,m+1)-2+mod)%mod;
    dfs(1,0);
    printf("%lld",ans);
}

T2

题解:

打表20pts
我们可以对这个矩阵做一个转化(i,j)->(i-j,j)
然后一个位置上的数字就必须在他上面&左边的数字都取了之后才能取
这个模型其实就是杨氏矩阵
2018SD省队集训R1 D1_第1张图片
杨氏矩阵,一个数字比右边的下面的大,除非右边下面没有数字了
钩子定理,用来求杨氏矩阵个数的算法
钩子长度:右边元素个数+下边元素个数+1
钩子公式:对于给定形状,不同的杨氏矩阵的个数为(n!/(每个格子的钩子长度的积))。
然后直接算答案就行了。你每次给格子染色,转化矩阵肯定T,那么我们可以预处理矩阵

代码:

T3

题解:

这种两个人玩游戏而且答案是数字,一个人要求最大值一个人要最小值的基本是对抗搜索了。
然后对抗搜索有40pts。
f[i][0/1]第i个数是正面/反面第一个人可以得到的权值,那么对于第二个人来说,要选择f[i+1]最小的,对于第一个人来说,要选择f[i+1]最大的。那么对于第二个人来说,如果他选择左边的,第一个人将会失去a[i]这个权值。

代码:

40pts

#include 
#include 
#define LL long long
using namespace std;
const int N=2005;
LL a[N],f[N][2];int n;
LL calc()
{
    f[n][0]=a[n]; f[n][1]=0;
    for (int i=n-1;i>=1;i--)
      if (i%2)
      {
        f[i][0]=max(f[i+1][0]+a[i],f[i+1][1]+a[i]);
        f[i][1]=max(f[i+1][0],f[i+1][1]+a[i]);
      }else 
      {
        f[i][0]=min(f[i+1][0],f[i+1][1]+a[i]);//下一位是朝上的,即我保了这一位,第一个人将失去a[i]权值;或者把下一位翻过来,自己会失去这个a[i]
        f[i][1]=min(f[i+1][0],f[i+1][1]);
      }
    return f[1][0];
}
int main()
{
//  freopen("game.in","r",stdin);
//  freopen("game.out","w",stdout);
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
    printf("%lld\n",calc());
    int q,x;scanf("%d",&q);  
    while (q--)
    {
        LL y;scanf("%d%lld",&x,&y);
        a[x]-=y;
        printf("%lld\n",calc());
    }
}

你可能感兴趣的:(省队集训,数学相关,搜索(dfs序),省队集训)