Problem A 

给出一个$n$个点$m$条边的仙人掌图(每条边最多处于一个简单环中)。

使用$c$种颜色对这张图中的顶点染色,使得每一条无向边连接的两个点颜色不同。

求染色的方案数,$mod \ 998244353$的值。

对于$100\%$的数据满足,$ 1 \leq n,m \leq 10^6$

Solution :

  对于一棵树的答案非常简单就是$c \times (c-1) ^ {n-1}$

  对于一个大环,我们不妨计算这个环上的方案数。

  设$f(n)$表示含有$n$个点的环上使用$c$种颜色染色的方案数,

  非常显然,$f(1) = c$ , $f(2) = c(c-1)$.

  若$c \geq 3$ 那么考虑对于所有节点不能和前一个刚被染色的节点重复染色,方案数就是$c \times (c-1)^{n-1}$

  然后考虑,最后一个点和第一个点的颜色不能重复,若重复,我们可以将其看做一个点,那么就变成了一个子问题。

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

  对于仙人掌图我们考虑v-DCC缩点(即找点双联通分量)  

  对于一个点,可能在多个简单环里。

  那么对于这个点,只要被一个环计算过,那么在计算剩余环的时候应当认为这个点的颜色既定。

  所以,若多于一个点在$k$个简单环里,我们只需要将总方案数直接除以$c^{k-1}$即可,这是由于该点在其他$k-1$个环中被认为可以被染$c$种颜色,然而事实上,这个点的颜色在第一个环包含它的时候已经被计算。

  所以,本题只需要进行一遍tarjan的v-DCC缩点即可。

  复杂度$O(n)$

# include <bits/stdc++.h>
# define int long long
using namespace std;
const int N=1e6+,mo=;
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<<];
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 = ;
while (n) {
if (n&) ans=ans*x%mo;
x=x*x%mo;
n>>=;
}
return ans%mo;
}
void tarjan(int u) {
dfn[u] = low[u] = ++dfn[]; 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=;i<=m;i++) {
int u,v; scanf("%lld%lld",&u,&v);
adde(u,v); adde(v,u);
}
f[] = c; f[] = c*(c-) % mo;
for (int i=;i<=n;i++)
f[i]=((c*Pow(c-,i-)%mo-f[i-])%mo+mo)%mo;
tarjan();
int ans = ,ret = ;
for (int i=;i<=n;i++) ret+=blo[i]-;
for (int i=;i<=cnt;i++)
ans = ans*f[dcc[i].size()]%mo;
int ni = Pow(c,mo-);
ans = ans * Pow(ni,ret) % mo;
printf("%lld\n",ans);
return ;
}

A.cpp

Problem B

有一个集合S,初始时为空.给定$n,k$

每一次操作,你可以选择1到n中的任意一个不在S中的元素i,并将其加入S,此时如果i-2也在S中,则将i-2从S中删除,

如果i+k也在S中,则将i+k从S中删除。该操作可以进行任意多次。郝仁想知道总共有多少种不同的S,对998244353取模

对于$100\%$的数据$n \leq 300$

Solution :

  首先将集合转化为一个有向图,对于每一个$i$若存在$i-2$和$i+k$,则从$i$连出一条边。

  这样本题就转化为一个图上的计数题。

  • 若$k$为偶数。

    显然序号为奇数和序号为偶数互不干扰,我们可以将奇数序号和偶数序号的分别进行考虑。

    问题便转化为有$n$个数里面选择若干个数,最多可以连续选择$m$个数字,方案数。

    最后将偶数的方案总数乘以奇数的方案总数即可。

    于是我们定义$f_{i,j}$表示前$i$个数字,当前已经连续选择$j$个数字,方案总数。

    转移可以采用刷表法:$f_{i+1,0}+=f_{i,j} , f_{i+1,j+1}+=f_{i,j}$

    复杂度是$O(nm)$的。

  • 若$k$为奇数。

    奇数序号和偶数序号相关,我们可以将$1  - n$的序号从小到大从上到下,奇数在左边,偶数在右边。

    $i$和$i+k$对齐,这样构成的矩阵,高是$\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=,mo=;
int n,k;
namespace Subtask1 {
int f[N][N][N];
void main() {
f[][][] = ;
for (int i=;i<(n+)/+k/;i++)
for (int j=;j<=n;j++)
for (int w = ; w <= k+ ; w++) {
(f[i+][][]+=f[i][j][w])%=mo;
if (i>=(k/)) (f[i+][][w?w+:]+=f[i][j][w])%=mo;
if (i<n/) (f[i+][j+][]+=f[i][j][w])%=mo;
if (i>=(k/) && i<(n/)) (f[i+][j+][max(w+,j+)]+=f[i][j][w])%=mo;
}
int ans = ;
for (int j=;j<=n;j++)
for (int w=;w<=k+;w++)
(ans+=f[(n+)/+k/][j][w])%=mo;
cout<<ans<<'\n';
exit();
}
}
namespace Subtask2 {
int f[N][N];
int g(int n,int m) {
memset(f,,sizeof(f)); f[][] = ;
for (int i=;i<=n;i++)
for (int j=;j<=m;j++) {
f[i+][]=(f[i+][]+f[i][j])%mo;
if (j+<=m) f[i+][j+]=(f[i+][j+]+f[i][j])%mo;
}
int ret=;
for (int i=;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/) * g(n/,k/) % mo;
cout<<ans<<'\n';
exit();
}
}
signed main() {
cin>>n>>k;
if (k&) Subtask1::main();
else Subtask2::main();
return ;
}

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

C.cpp

HGOI 20190821 慈溪一中互测的更多相关文章

  1. HGOI 20190816 省常中互测8

    Problem A  有两条以(0,0)为端点,分别经过(a,b),(c,d)的射线,你要求出夹在两条射线中间,且距离(0,0)最近的点(x,y) 对于$100\%$的数据满足$1 \leq T \l ...

  2. 【loj2461】【2018集训队互测Day 1】完美的队列

    #2461. 「2018 集训队互测 Day 1」完美的队列 传送门: https://loj.ac/problem/2461 题解: 直接做可能一次操作加入队列同时会弹出很多数字,无法维护:一个操作 ...

  3. 【2018集训队互测】【XSY3372】取石子

    题目来源:2018集训队互测 Round17 T2 题意: 题解: 显然我是不可能想出来的……但是觉得这题题解太神了就来搬(chao)一下……Orzpyz! 显然不会无解…… 为了方便计算石子个数,在 ...

  4. 【CH 弱省互测 Round #1 】OVOO(可持久化可并堆)

    Description 给定一颗 \(n\) 个点的树,带边权. 你可以选出一个包含 \(1\) 顶点的连通块,连通块的权值为连接块内这些点的边权和. 求一种选法,使得这个选法的权值是所有选法中第 \ ...

  5. 洛谷 P4463 - [集训队互测 2012] calc(多项式)

    题面传送门 & 加强版题面传送门 竟然能独立做出 jxd 互测的题(及其加强版),震撼震撼(((故写题解以祭之 首先由于 \(a_1,a_2,\cdots,a_n\) 互不相同,故可以考虑求出 ...

  6. EZ 2018 05 06 NOIP2018 慈溪中学集训队互测(五)

    享受爆零的快感 老叶本来是让初三的打的,然后我SB的去凑热闹了 TM的T2写炸了(去你妹的优化),T1连-1的分都忘记判了,T3理所当然的不会 光荣革命啊! T1 思维图论题,CHJ dalao给出了 ...

  7. LOJ3069. 「2019 集训队互测 Day 1」整点计数(min_25筛)

    题目链接 https://loj.ac/problem/3069 题解 复数真神奇. 一句话题意:令 \(f(x)\) 表示以原点 \((0, 0)\) 为圆心,半径为 \(x\) 的圆上的整点数量, ...

  8. Alpha2的项目互评互测

    目录 @(Alpha2项目测试) 这个作业属于哪个课程 课程链接 这个作业要求在哪里 作业要求的链接 团队名称 你的代码我的发 这个作业的目标 其他参考文献 软件测试用例 姓名 学号 团队名称 李涵 ...

  9. tree (一本通练习||清华集训互测)

    tree 内存限制:512 MiB 时间限制:3000 ms 标准输入输出 题目类型:传统 评测方式:文本比较   题目描述 给你一个无向带权连通图,每条边是黑色或白色.让你求一棵最小权的恰好有nee ...

随机推荐

  1. python装饰器的原理

    装饰器的原理就是利用<闭包函数>来实现,闭包函数的原理就是包含内层函数的return和外层环境变量:

  2. 在.Net中使用RedLock实现分布式锁

    ⒈简介 RedLock 分布式锁算法由 Redis 的作者提出,大部分语言都有对应的实现,查看,RedLock.net 是 RedLock 分布式锁算法的 .NET 版实现,用来解决分布式下的并发问题 ...

  3. Tensorflow常见函数case argmax equal

    常用的函数: tf.argmax(input, axis=None, name=None, dimension=None) input:输入Tensor axis:0表示按列,1表示按行 name:名 ...

  4. python-redis-订阅和发布

    发布:redishelper.py import redis class RedisHelper: def __init__(self): self.__conn = redis.Redis(host ...

  5. Sleepy Game CodeForces - 936B

    大意: 给定有向图, 初始点S, 两个人轮流移动, 谁不能移动则输, 但后手睡着了, 先手可以控制后手操作, 求最后先手结果. 刚开始看错了, 还以为后手也是最优策略.... 实际上判断是否有偶数个节 ...

  6. ubuntu - 如何以root身份使用图形界面管理文件?

    nautilus 是gnome的文件管理器,但是如果不是root账号下,权限受限,我们可以通过以下方式以root权限使用! 一,快捷键“ctrl+alt+t”,调出shell. 二,在shell中输入 ...

  7. Linux上安装postgres 10.5

    由于接触了华为的elk大数据平台,里面封装的是postgres ,就想着安装一下,熟悉一下postgres数据. 安装包下载:https://www.postgresql.org/ftp/source ...

  8. Hybrid APP架构设计

    通讯 作为一种跨语言开发模式,通讯层是Hybrid架构首先应该考虑和设计的,往后所有的逻辑都是基于通讯层展开. Native(以Android为例)和H5通讯,基本原理: Android调用H5:通过 ...

  9. java多线程ExecutorService

    1.new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? new Thread(new Runnable() { @Override public void run() { ...

  10. linux系统查看某个用户错误登录次数

    pam_tally2 --user user_name 查看user_name用户的错误登录次数 pam_tally2 --user user_name --reset 清空user_name用户的错 ...