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

求逆序对个数便可。

相关文章
相关标签/搜索