HGOI 20190821 Cixi in a mutual test

Problem A 

Given a point $ $ $ n-m $ cacti of edges (each edge in a most simple ring).

$ C $ using vertex color staining of this figure, so that two color points per edge are connected to a different free.

Seeking stained program number, $ mod \ value of $ 998,244,353.

For $ 100 \ $ data satisfies%, $ 1 \ leq n, m \ leq 10 ^ 6 $

Solution : 

  For a answer is simple tree is $ c \ times (c-1) ^ {n-1} $

  For a large ring, we may wish to calculate the number of programs on the ring.

  Provided $ f (n) $ represents the number of programs using colors $ c $ stained ring containing n-$ $ points,

  Very clearly, $ f (1) = c $, $ f (2) = c (c-1) $.

  If the $ c \ geq 3 $ then consider for all the nodes and the previous node just repeated staining staining, the number of programs is $ c \ times (c-1) ^ {n-1} $

  Then consider the color of the last point and the first point can not be repeated, if repeated, we can be seen as a point, then it becomes a problem child.

  Finally, $ f (n) = c \ times (c-1) ^ {n-1} - f (n-1) $

  For cacti we consider the v-DCC condensing point (i.e., point to find dual component Unicom)  

  For a point where the ring may be a plurality of simple.

  So for this point, as long as one ring is calculated, then calculating the remaining ring should be considered when the predetermined color point.

  Therefore, if more than one point in the ring $ k $ simple, we only need to direct the program divided by the total number of $ c ^ {k-1} $ can, since the other points $ k-1 $ ring is believed to be transfected $ c $ colors, but in fact, it contains the color points of time have been calculated in the first loop.

  So, this question requires only once tarjan of v-DCC shrink to the point.

  Complexity $ O (n) $

# include <bits/stdc++.h>
# define int long long
using namespace std;
const int N=1e6+10,mo=998244353;
int dfn[N],low[N],n,m,tot,head[N],cnt,blo[N],f[N],c;
vector<int>dcc[N];
stack<int>s;
struct rec{
    int pre,to;
}a[N<<2];
void adde(int u,int v) {
    a[++tot].pre=head[u];
    a[tot].to=v;
    head[u]=tot;
}
int Pow(int x,int n) {
    int ans = 1;
    while (n) {
        if (n&1) ans=ans*x%mo;
        x=x*x%mo;
        n>>=1;
    }
    return ans%mo;
}
void tarjan(int u) {
    dfn[u] = low[u] = ++dfn[0]; s.push(u);
    for (int i=head[u];i;i=a[i].pre){
        int v=a[i].to; 
        if (!dfn[v]) {
            tarjan(v); low[u]=min(low[u],low[v]);
            if (low[v]>=dfn[u]) {
                int z; ++cnt;
                do {
                    z = s.top(); s.pop();
                    dcc[cnt].push_back(z);
                    blo[z]++;
                }while (v!=z);
                dcc[cnt].push_back(u);
                blo[u]++;
            }
        } else low[u] = min(low[u],dfn[v]);
    }
}
 main()
{
    scanf("%lld%lld%lld",&n,&m,&c);
    for (int i=1;i<=m;i++) {
        int u,v; scanf("%lld%lld",&u,&v);
        adde(u,v); adde(v,u);
    }
    f[1] = c; f[2] = c*(c-1) % mo;
    for (int i=3; i <= n; i ++ ) 
        f [i] = ((c * Pow (c- 1 , i- 1 )% mo-f [i- 1 ]) + mo mo%)% m; 
    Tarjan ( 1 );
    int years = one pays = 0 ;
    for ( int i = 1 ; i <= n; i ++) = + network blo [i] - 1 ;
    for ( int i = 1 ; i <= cnt; i ++ ) 
        ans = ans f * [DCC [i] .size ()]% m;
    int or = Pow (c mo- 2 );   
    ans = ans * Pow (or pays)% mo;
    printf("%lld\n",ans);
    return 0;
}
A.cpp

Problem B

There is a set S, is initially empty. Given $ n, k $

Each operation, you can select any one of the elements i to n are not in a S, S and added, also in this case if S i-2, the i-2 will be removed from the S,

If S i + k also, the i + k then removed from the S. This operation can be performed any number of times. Hao Ren would like to know how many different total S, for modulo 998 244 353

For $ 100 \% $ data $ n \ leq 300 $

Solution : 

  First, it sets into a directed graph, for each $ I $, if present, $ i-2 $ and $ i + k $, $ I $ even out from one side.

  Thus the present problem is converted to the title on a count of FIG.

  • If $ k $ even.

    Obviously odd number and serial number is even interfere with each other, we can be odd-numbered and even-numbered separately considered.

    Problems will be converted into the number of which has $ $ select a number of n-number, up to $ m $ continuous selection numbers, the number of programs.

    Finally, the program will total an even number multiplied by the total number of programs can be odd.

    So we define $ f_ {i, j} $ I $ $ represents the front digit, currently $ J $ selected consecutive numbers, the total number of programs.

    Transfer brush can be employed table method: $ f_ {i + 1,0} + = f_ {i, j}, f_ {i + 1, j + 1} + = f_ {i, j} $

    Complexity is $ O (nm) $ a.

  • If $ k $ is odd.

    Odd-numbered and even-numbered-related, we can $ 1 - n $ serial number from small to large from top to bottom, on the left odd, even number on the right.

    $ I $ and $ i + k $ aligned matrix thus constituted, the high is $ \ left \ lfloor \ frac {n + 1} {2} \ right \ rfloor + \ left \ lfloor \ frac {k} {2} \ right \ rfloor $

    其中,右侧偶数非空的区间是$[1,\left \lfloor \frac{n}{2}  \right \rfloor]$

    左侧奇数的非空区间是$[\left \lfloor \frac{k}{2} \right \rfloor , \left \lfloor \frac{n+1}{2} \right \rfloor+ \left \lfloor \frac{k}{2} \right \rfloor]$

    上述结论是基本性质,归纳可证。

    考虑一个dp,设$f[i][j][k]$表示当前已经考虑到第$i$层了(从上到下,层数从$1$ 到 $\left \lfloor \frac{n+1}{2} \right \rfloor+ \left \lfloor \frac{k}{2} \right \rfloor$), 在右侧偶数已经连续选择了$j$个元素(为0则为空),在左侧奇数最多连续选择了$k$个元素,且必须包含一个拐弯(我们认为从偶数列跳到奇数列为一个"拐弯")

    我们考虑第$i$层,左选/不选,右选/不选的情况,就可以得到四个DP方程。

    即

      • (左右都不选择,无限制)  : f[i+1][0][0]=f[i][j][k]
      • (左选右不选择,左边的元素存在,即$i \in [\left \lfloor \frac{k}{2} \right \rfloor , \left \lfloor \frac{n+1}{2} \right \rfloor+ \left \lfloor \frac{k}{2} \right \rfloor]$)

       $ f[i+1][0][k?k+1:0]+=f[i][j][k]$ , 若原来没有或者没有拐弯,那么现在也不会有。

      • (左不选右选,右边的元素存在,即$i \in [1,\left \lfloor \frac{n}{2}  \right \rfloor )$)

       $f[i+1][j+1][0]+=f[i][j][w]$ 

      • (既选择左,又选择右侧元素,左右元素同时存在,上述两个集合的交集)

$f[i+1][j+1][max(w+1,j+2)]+=f[i][j][w]$ , 左侧的状态有两条路来走,我们取较大的一条。

上述DP细节较多,可以采用刷表实现,复杂度会是$O(n^3)$. 

# include <bits/stdc++.h>
# define int long long
using namespace std;
const int N=305,mo=998244353;
int n,k;
namespace Subtask1 {
    int f[N][N][N];
    void main() {
        f[0][0][0] = 1;
        for (int i=0;i<(n+1)/2+k/2;i++)
         for (int j=0;j<=n;j++)
          for (int w = 0 ; w <= k+1 ; w++) {
            (f[i+1][0][0]+=f[i][j][w])%=mo;
            if (i>=(k/2)) (f[i+1][0][w?w+1:0]+=f[i][j][w])%=mo;
            if (i<n/2) (f[i+1][j+1][0]+=f[i][j][w])%=mo;
            if (i>=(k/2) && i<(n/2)) (f[i+1][j+1][max(w+1,j+2)]+=f[i][j][w])%=mo;
          }
        int ans = 0;
        for (int j=0;j<=n;j++)
         for (int w=0;w<=k+1;w++)
          (ans+=f[(n+1)/2+k/2][j][w])%=mo;
        cout<<ans<<'\n';
        exit(0);
    }
}
namespace Subtask2 {
    int f[N][N];
    int g(int n,int m) {
        memset(f,0,sizeof(f)); f[0][0] = 1;
        for (int i=0;i<=n;i++)
        for (int j=0;j<=m;j++) {
            f[i+1][0]=(f[i+1][0]+f[i][j])%mo;
            if (j+1<=m) f[i+1][j+1]=(f[i+1][j+1]+f[i][j])%mo;
        }
        int ret=0;
        for (int i=0;i<=m;i++) ret=(ret+f[n][i])%mo;
        return ret % mo;
    }
    void main() {
        int ans = 1ll * g((int)ceil((double)n/2.0),k/2) * g(n/2,k/2) % mo;
        cout<<ans<<'\n';
        exit(0);
    }
}
signed main() {
    cin>>n>>k;
    if (k&1) Subtask1::main();
    else Subtask2::main();
    return 0;
}
B.cpp

 Problem C      

  维护序列$a_i$支持$q$次查询操作,形如$l,r,d$表示在区间$[l,r]$中是$d$倍数的数的个数。

  对于$100\%$的数据满足$n,q,a_i \leq 10^5$

  Solution : 直接暴力分块,每一块中维护这个块内约数为$i , i \in [1,10^5]$的数的个数,方便$O(1)$查询。

       对于块外元素,直接暴力,对于块内元素,直接跑块即可。

       时空复杂度都是$O(n \sqrt{n})$  。

# pragma GCC optimize(3)
# include <bits/stdc++.h>
# define Rint register int
using namespace std;
const int N=1e5+1;
struct rec{
    int l,r,cnt[N];
}tr[318];
int n,m,a[N],blong[N],block,num;
inline int read()
{
    int X=0; char c=0;
    while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') X=(X<<3)+(X<<1)+(c^48),c=getchar();
    return X;
}
void write(Rint x) {
    if (x>9) write(x/10);
    putchar('0'+x%10);
}
inline void writeln(Rint x) {
    write(x); putchar('\n');
}
int query(int l,int r,int d)
{
    int ret = 0;
    if (blong[l] == blong[r]) {
        for (Rint i=l;i<=r;i++)
         if (a[i]%d == 0) ret++;
        return ret; 
    }
    for (Rint i=l;i<=tr[blong[l]].r;i++) 
     if (a[i]%d == 0) ret++;
    for (Rint i=tr[blong[r]].l;i<=r;i++) 
     if (a[i]%d == 0) ret++;
    for (Rint i = blong[l]+1 ; i<=blong[r]-1; i++) 
     if (d<=n) ret+=tr[i].cnt[d];
    return ret;  
}
int main()
{
    n = read(); m =read();
    for (Rint i=1;i<=n;i++) a[i]=read();
    block=sqrt(n); 
    num=n/block; if (n%block) num++;
    for (Rint i=1;i<=num;i++) {
        tr[i].l=(i-1)*block+1,
        tr[i].r=i*block;
    }
    tr[num].r=n;
    for (Rint i=1;i<=n;i++) {
        blong[i]=(i-1)/block+1;
        for (Rint j=1;j<=sqrt(a[i]);j++) {
            if (a[i]%j!=0) continue; 
            tr[blong[i]].cnt[j]++; 
            if (j*j!=a[i]) tr[blong[i]].cnt[a[i]/j]++;
        } 
    }
    int l,r,d;
    while (m--) {
        l = read(),r =read(),d=read(); 
        query(l,r,d);
        writeln(query(l,r,d));
    }
    return 0;
}
C.cpp

 

Guess you like

Origin www.cnblogs.com/ljc20020730/p/11391007.html