2018SD省隊集訓R1 D1

T1

題解:

經過每條邊至少一次並且回到原點,我們可以想到歐拉回路。發現歐拉回路的特點是每個點度數爲偶數,然後我們的目標就是把度數奇數的那些點通過添加一些長度儘量小的邊變成度數爲偶數。
添加哪些邊呢?不難想到是最小生成樹上的邊,那我們先添加成最小生成樹,其實就是每條邊可以選/不選,每個點的要求被選的相鄰邊是奇數/偶數。最小化權值,自底向上dfs一遍就好了

代碼:

#include <cstdio>
#include <iostream>
#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)
然後一個位置上的數字就必須在他上面&左邊的數字都取了之後才能取
這個模型其實就是楊氏矩陣
這裏寫圖片描述
楊氏矩陣,一個數字比右邊的下面的大,除非右邊下面沒有數字了
鉤子定理,用來求楊氏矩陣個數的算法
鉤子長度:右邊元素個數+下邊元素個數+1
鉤子公式:對於給定形狀,不同的楊氏矩陣的個數爲(n!/(每個格子的鉤子長度的積))。
然後直接算答案就行了。你每次給格子染色,轉化矩陣肯定T,那麼我們可以預處理矩陣

代碼:

T3

題解:

這種兩個人玩遊戲而且答案是數字,一個人要求最大值一個人要最小值的基本是對抗搜索了。
然後對抗搜索有40pts。
f[i][0/1]第i個數是正面/反面第一個人可以得到的權值,那麼對於第二個人來說,要選擇f[i+1]最小的,對於第一個人來說,要選擇f[i+1]最大的。那麼對於第二個人來說,如果他選擇左邊的,第一個人將會失去a[i]這個權值。

代碼:

40pts

#include <cstdio>
#include <iostream>
#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());
    }
}
發佈了722 篇原創文章 · 獲贊 547 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章