2018 Multi-University Training Contest 2

傳送門ios

C - Cover

題意:
給出一張無向圖,如今問最少多少次「一筆畫」可以覆蓋全部的邊,而且輸出方案數。c++

思路:算法

  • 在無向圖中,存在歐拉路徑當且僅當全部點度數都爲偶數或者只有兩個點的度數爲奇數而且一個點爲起點一個點爲終點。
  • 由於一次「一筆畫」最多消掉兩個奇數點,因此最少的次數就爲\(max(\frac{d}{2},1),d\)表示奇數點個數。
  • 關鍵在於怎麼構造方案。
  • 最直接的想法就是將奇數點兩兩配對,而後每次求一遍歐拉回路,但很差控制終點。
  • 題解的作法就比較神奇,經過添加一些虛邊來鏈接奇數點,那麼如今全部點的度數就爲偶數。以後從起點\(dfs\),若遇到虛邊則答案++,不然就將邊加入當且答案中。
  • 我也不太清楚爲何這樣可以扣出全部的方案,但若經過虛邊找到一個環,那麼說明確定找到一個方案,回溯時若一個點沒有其它邊,說明它確定在這個環上;不然,還有其它路徑,這個點可能會在其它路徑上。
  • 因此這個的正確性或許就是,一條邊被多個方案覆蓋的話,選擇哪一種都行。

尚未寫代碼,就貼貼標程吧,兩次dfs,第一次搜出連通塊而且找到奇數點,第二次就執行相似於上述的算法。
ui


Code

#include<bits/stdc++.h>
using namespace std;
int n,m,w[100010],a[100010],b[300010],c[300010],d[300010],r=1,p;
vector<int> f,x[100010];
bool u[100010];
inline void add_(int i,int j)
{
    w[i]++;
    b[++r]=j;
    c[r]=a[i];
    a[i]=r;
}
inline void add(int i,int j)
{
    add_(i,j);
    add_(j,i);
}
inline void dfs(int i)
{
    int j;
    u[i]=1;
    if(w[i]&1)
      f.push_back(i);
    for(j=a[i];j;j=c[j])
      if(!u[b[j]])
        dfs(b[j]);
}
inline void dfs2(int i)
{
    int j;
    for(j=a[i];j;j=c[j])
      if(!d[j])
        {
         d[j]=d[j^1]=1;
         dfs2(b[j]);
         if(j>2*m+1)
           p++;
         else
           x[p].push_back(j/2*(2*(j&1)-1));
        }
}
int main()
{
    int i,j,k;
    while(scanf("%d%d",&n,&m)!=EOF)
      {
       for(i=1;i<=m;i++)
         {
          scanf("%d%d",&j,&k);
          add(j,k);
         }
       for(i=1;i<=n;i++)
         if(!u[i] && w[i])
           {
            dfs(i);
            if(!f.size())
              {
               f.push_back(i);
               f.push_back(i);
              }
            for(j=2;j<f.size();j+=2)
              add(f[j],f[j+1]);
            p++;
            dfs2(f[0]);
            f.clear();
           }
       printf("%d\n",p);
       for(i=1;i<=p;i++)
         {
          printf("%d ",x[i].size());
          for(j=0;j<x[i].size();j++)
            printf("%d ",x[i][j]);
          printf("\n");
          x[i].clear();
         }
       for(i=1;i<=n;i++)
         a[i]=w[i]=u[i]=0;
       for(i=1;i<=r;i++)
         d[i]=0;
       p=0;
       r=1;
      }
    return 0;
}

D - Game

輸出"YES"便可。spa

E - Hack It

題意:
構造\(01\)長寬都爲\(n,n\leq 2000\)的矩陣,保證矩陣中不存在四個角都爲\(1\)的矩形,而且\(1\)的個數很多於\(85000\).net

思路:
構造十分神奇,我是想不出來的QAQ。
首先對矩陣分塊,咱們取一個素數\(p=47\),其實就至關於\(\sqrt{n}\)的樣子,那麼如今就有\(p*p\)個小矩形,每一個矩形大小爲\(p*p\)
若每行每列都只放一個,也就是每一個小矩形就放\(p\)\(1\),那麼最終的答案就有\(p^3\)\(1\),也差很少就是\(85000\)了。
而後構造的時候就循環放置,只保證存在一列有重複,其餘列都各不重複,能夠證實當\(p\)爲素數時是存在方案的(這也就是爲啥選素數的緣由)。
好比對於\(p=5\)的狀況,咱們就相似於這樣構造:code

10000 10000 10000 10000 10000
10000 01000 00100 00010 00001
10000 00100 00001 01000 00010
10000 00010 01000 00001 00100
10000 00001 00010 00100 01000

01000 ...
01000 ...
...

能夠證實,這樣放置確定不會存在重複,最終找到規律,只須要讓\(a[kp+b][ip+(ki+b)\% p]=1\)便可。
證實挺好證的,構造四個點而後列等式便可,發現只有當\(p\)爲質數時纔不矛盾(可是一開始怎麼想獲得這是素數...)ip

這個題大概就這麼作...我感受想到素數分塊這一步很難,多是我積累少了吧...ci

upd:剛纔發現這個可能和徹底剩餘繫有關,設\(\{a_k\}\)爲模\(p\)的徹底剩餘系,那麼對於任意\(gcd(p,x)=1\)\(x\),都有\(\{xa_k\}\)爲另外一個徹底剩餘系,而且知足題中條件。
證實以下:
假設如今有\(x*a_i\equiv x*a_j\),那麼即有\(x*(a_i-a_j)\equiv 0\),由於\(gcd(x,p)=1\),那麼就有\(a_i\equiv a_j\)。也就是說當\(a_j\not ={a_i}\)時,有\(xa_j\not ={xa_i}\)
那麼咱們能夠將\(p\)行爲一塊獨立來看,除開全部都相同的那一列,其他的列都構成多個徹底剩餘系,也就是不會出現重複的。
多個塊之間由於最多加\(p-1\),因此也不會出現重複。get

感性證實就完成啦(爲啥感受寫了一萬年)

G - Naive Operations

題意:
給出兩個序列\(a,b\),一開始\(a\)全爲\(0\)\(b\)爲一個排列。而後有兩個操做:

  • \("add\ l\ r"\):\(a_l,a_{l+1},\cdots,a_r\)都加上1;
  • \("query\ l\ r"\):詢問\(\sum_{i=l}^r\lfloor\frac{a_i}{b_i}\rfloor\)

思路:
線段樹維護兩個值便可,一個爲答案,另外一個爲還剩多少就會\(+1\)的最小值。若是當前要\(+1\),線段樹暴力更新便可。
能夠知道\(+1\)不會超過\(\sum_{i=1}^n\lfloor\frac{n}{b_i}\rfloor=O(logn)\)次,因此複雜度大概\(O(nlog^2n)\)的樣子。


Code

#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
const int MAXN = 1e5+5,MAXM = 4e5+5,MOD = 100003,INF = 0x3f3f3f3f;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
const db eps = 1e-9;
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define mid l + ((r-l)>>1)
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define pii pair<int,int>
#define vii vector<pii>
#define vi vector<int>
using namespace std;
int n,q,b[MAXN],dif[MAXN<<2],mv[MAXN<<2];
ll sumv[MAXN<<2];
inline void pushUp(int o){
    dif[o]=min(dif[o<<1],dif[o<<1|1]);
    sumv[o]=sumv[o<<1] + sumv[o<<1|1];
}
void build(int o,int l,int r){
mv[o]=0;
    if(l==r){
        dif[o] = b[l];
        sumv[o]=mv[o]=0;
        return ;
    }
    int m=mid;
    build(lson);build(rson);
    pushUp(o);
}
inline void pushDown(int o){
    if(mv[o]){
        mv[o<<1] += mv[o];
        mv[o<<1|1] += mv[o];
        dif[o<<1] -=mv[o];
        dif[o<<1|1]-=mv[o];
        assert(dif[o<<1]>=0);
        assert(dif[o<<1|1]>=0);
        mv[o]=0;
    }
}
void update2(int o,int l,int r){
    if(l==r){
        dif[o] = b[l];mv[o]=0;
        sumv[o]++;
        return ;
    }
    int m=mid;
    pushDown(o);
    if(dif[o<<1]==0)update2(lson);
    if(dif[o<<1|1]==0)update2(rson);
    pushUp(o);
}
void update(int o,int l,int r,int L,int R){
    if(l>=L&&r<=R){
        dif[o]--;mv[o]++;
        if(dif[o]==0)update2(o,l,r);
        return ;
    }
    int m=mid;
    pushDown(o);
    if(L<=m)update(lson,L,R);
    if(R>m)update(rson,L,R);
    pushUp(o);
}
ll query(int o,int l,int r,int L,int R){
    if(dif[o]==0)update2(o,l,r);
    if(l>=L&&r<=R){
        return sumv[o];
    }
    int m=mid;
    ll ans=0;
    pushDown(o);
    if(L<=m)ans+=query(lson,L,R);
    if(R>m)ans+=query(rson,L,R);
    return ans;
}
int main(){
    ios::sync_with_stdio(false);
    while(cin>>n>>q){
        for(int i=1;i<=n;i++)cin>>b[i];
        build(1,1,n);
        while(q--){
            static char op[7];
            static int l,r;
            cin>>op>>l>>r;
            if(op[0]=='a')update(1,1,n,l,r);
            else cout<<query(1,1,n,l,r)<<'\n';
        }
    }
    return 0;
}

J - Swaps and Inversions

求逆序對個數便可。

相關文章
相關標籤/搜索