【学习笔记】AGC009/AGC019/AGC029/AGC035
创始人
2024-04-06 02:24:36
0

AGC009

Eternal Average

奥妙重重

如果我们用树的结构来描述操作,设一个点的深度为xix_ixi​,那么一定满足∑k−xi=1\sum k^{-x_i}=1∑k−xi​=1,并且 Z=∑ai=1k−xiZ=\sum_{a_i=1}k^{-x_i}Z=∑ai​=1​k−xi​。

方便起见,我们引入kkk进制小数的概念。设Z=0.z1z2...znZ=0.z_1z_2...z_nZ=0.z1​z2​...zn​,其中ziz_izi​表示k−ik^{-i}k−i,zn≠0z_n\ne 0zn​=0 。

那么合法的小数部分{zi}\{z_i\}{zi​}应该满足:

1.11.11.1 如果不进位,那么∑i=1nzi=n\sum_{i=1}^n z_i= n∑i=1n​zi​=n
1.21.21.2 如果进位,那么∑i=1nzi≤n\sum_{i=1}^nz_i\le n∑i=1n​zi​≤n ,并且每次进位会导致数位之和减去k−1k-1k−1,∑i=1nzi≡n(modk−1)\sum_{i=1}^n z_i\equiv n\pmod {k-1}∑i=1n​zi​≡n(modk−1) 。

假设上述限制是充要的。换句话说,我们只关注ZZZ最终的形式,而不关注具体的方案。

不妨做一些逆向思考。知道了ZZZ的最终形式后,通过不断拆分高位可以得到恰好nnn个数,而根据这nnn个点的深度又可以把实际的操作树求出来。具体操作是,从根节点开始分裂,在每一层保留恰当的点数,不难自行验证。扯得有点远了

最后不要忘了∑k−xi=1\sum k^{-x_i}=1∑k−xi​=1这个条件,事实上这等价于∑ai=0k−xi=1−Z=0.(k−1−z1)(k−1−z2)...(k−zn)\sum_{a_i=0}k^{-x_i}=1-Z=0.(k-1-z_1)(k-1-z_2)...(k-z_n)∑ai​=0​k−xi​=1−Z=0.(k−1−z1​)(k−1−z2​)...(k−zn​) ,根据上述推导得出1+∑i=1n(k−1−zi)≤m1+\sum_{i=1}^n(k-1-z_i)\le m1+∑i=1n​(k−1−zi​)≤m并且1+∑i=1n(k−1−zi)≡m(modk−1)1+\sum_{i=1}^n(k-1-z_i)\equiv m\pmod {k-1}1+∑i=1n​(k−1−zi​)≡m(modk−1) 。

让我们总结一下。设dpi,jdp_{i,j}dpi,j​表示当前从高到低考虑到第iii位,数位之和为jjj的方案数。转移方程为dpi,j=∑dpi−1,j−kdp_{i,j}=\sum dp_{i-1,j-k}dpi,j​=∑dpi−1,j−k​ ,可以前缀和优化。因为zn≠0z_n\ne 0zn​=0所以可能要用0/10/10/1记录一下当前这位的状态。

复杂度O(n2)O(n^2)O(n2)。

#include
#define ll long long
#define pb push_back
#define fi first
#define se second
#define db double
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
const int mod=1e9+7;
int n,m,K;
ll dp[2005][2005][2],sum[2005][2005],res;
void add(ll &x,ll y){x=(x+y)%mod;}
int main(){cin>>n>>m>>K;dp[0][0][0]=1;for(int i=0;i<=n;i++)sum[0][i]=1;for(int i=1;i<=(n+m-1)/(K-1);i++){for(int j=0;j<=n;j++){add(dp[i][j][0],dp[i-1][j][0]+dp[i-1][j][1]);if(min(K-1,j)==j)add(dp[i][j][1],sum[i-1][j-1]);else add(dp[i][j][1],sum[i-1][j-1]-sum[i-1][j-min(K-1,j)-1]);if(j%(K-1)==n%(K-1)&&i*(K-1)-j+1<=m&&(i*(K-1)-j+1)%(K-1)==m%(K-1))add(res,dp[i][j][1]);}sum[i][0]=1;for(int j=1;j<=n;j++)sum[i][j]=(sum[i][j-1]+dp[i][j][0]+dp[i][j][1])%mod;}cout<<(res+mod)%mod;
}

AGC019

Yes or No

组合数学题。

因为本人数学比较菜所以只会组合意义啦。

方便起见,我们引入坐标(x,y)(x,y)(x,y)表示当前状态。那么对于一条路径,猜对的问题数目就是蓝边的数量。

请添加图片描述
如果这条路径多次穿过y=xy=xy=x,那么我们只保留第一个交点,并对路径作对称,这样蓝边的数目并不会改变。

请添加图片描述
设交点坐标是(x,x)(x,x)(x,x),发现蓝边的数目(n−x)+x=n(n-x)+x=n(n−x)+x=n是定值!最后我们认为对角线的贡献是路径与对角线的交点数目/2

答案是n+∑i=1m(2ii)(n+m−2in−i)2(n+mn)n+\frac{\sum_{i=1}^m\binom{2i}{i}\binom{n+m-2i}{n-i}}{2\binom{n+m}{n}}n+2(nn+m​)∑i=1m​(i2i​)(n−in+m−2i​)​。

复杂度O(n)O(n)O(n)。

remark\text{remark}remark 妙处在于发现潜在的组合意义。

#include
#define ll long long
#define pb push_back
#define fi first
#define se second
#define db double
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
const int mod=998244353;
int n,m;
ll fac[1000005],inv[1000005],res;
ll fpow(ll x,ll y){ll z(1);for(;y;y>>=1){if(y&1)z=z*x%mod;x=x*x%mod;}return z;
}
void init(int n){fac[0]=1;for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%mod;inv[n]=fpow(fac[n],mod-2);for(int i=n;i>=1;i--)inv[i-1]=inv[i]*i%mod;
}
ll binom(int x,int y){return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
int main(){ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);cin>>n>>m;if(n

AGC029

Wandering TKHS

考虑uuu到根节点的路径为{t1,t2,...,tk}\{t_1,t_2,...,t_k\}{t1​,t2​,...,tk​},当从tit_iti​走到ti+1t_{i+1}ti+1​时,会扩展子树内比ti+1t_{i+1}ti+1​小的节点。因此想到设dpidp_idpi​表示从iii走到faifa_ifai​时,子树内扩展了多少个点。

请添加图片描述

记F(u,v)F(u,v)F(u,v)表示从uuu到vvv的路径上不包括vvv的最大的点,那么v∈subtree(u)v\in subtree(u)v∈subtree(u)有贡献等价于F(v,u)

考虑自底向上维护,假设x∈son(u)x\in son(u)x∈son(u),对于小于xxx的路径直接设为xxx,用(val,c)(val,c)(val,c)记录权值为valvalval的路径有ccc条,可以手写平衡树,复杂度O(nlog⁡2n)O(n\log^2 n)O(nlog2n) 。当然,线段树合并是更优秀的写法,复杂度O(nlog⁡n)O(n\log n)O(nlogn)。

#include
#define ll long long
#define pb push_back
#define fi first
#define se second
#define db double
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
int n,vis[200005],dep[200005],res[200005],dis[200005],rt[200005],dp[200005],f[200005],tot;
struct node{int l,r,sum;
}t[20000005];
vectorg[200005];
int ask(int p,int l,int r,int ql,int qr){if(!p)return 0;if(ql<=l&&r<=qr)return t[p].sum;int mid=l+r>>1;if(qr<=mid)return ask(t[p].l,l,mid,ql,qr);if(midif(!p)p=++tot;t[p].sum+=y;if(l==r)return;int mid=l+r>>1;x<=mid?ins(t[p].l,l,mid,x,y):ins(t[p].r,mid+1,r,x,y);
}
void del(int &p,int l,int r,int ql,int qr){if(!p)return;if(ql<=l&&r<=qr){p=0;return;}int mid=l+r>>1;if(ql<=mid)del(t[p].l,l,mid,ql,qr);if(midif(!p||!q)return p+q;t[p].sum+=t[q].sum;t[p].l=merge(t[p].l,t[q].l),t[p].r=merge(t[p].r,t[q].r);return p; 
}
void dfs(int u,int topf){for(auto v:g[u]){if(v!=topf){dis[v]=max(dis[u],u),dep[v]=dep[u]+1,dfs(v,u);int tot=ask(rt[v],1,n,1,v-1);del(rt[v],1,n,1,v-1),ins(rt[v],1,n,v,tot+1);dp[u]+=(f[v]=ask(rt[v],1,n,1,dis[u]-1)),rt[u]=merge(rt[u],rt[v]);}}
}
void dfs2(int u,int topf,int sum){res[u]=sum+dp[u];for(auto v:g[u]){if(v!=topf)dfs2(v,u,sum+dp[u]-f[v]);}
}
signed main(){ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);cin>>n;for(int i=1;iint u,v;cin>>u>>v,g[u].pb(v),g[v].pb(u);}for(auto u:g[1])dfs(u,1);for(auto u:g[1])dfs2(u,1,0);for(int i=2;i<=n;i++)cout<

Construction of a tree

神仙构造题。

我们首先构造二分图,左边是nnn个节点,右边是n−1n-1n−1个集合,(u,Ei)(u,E_i)(u,Ei​)有边当且仅当u∈Eiu\in E_iu∈Ei​。

然后有解的必要条件是,对于右边任意S⊆{E1,E2,...,En−1}S\subseteq\{E_1,E_2,...,E_{n-1}\}S⊆{E1​,E2​,...,En−1​},设其对应的点集为f(S)f(S)f(S),都有∣S∣

事实上这是充要的。给出如下构造。

首先求一个大小为n−1n-1n−1的匹配。钦定左边没有被匹配的vvv为根节点,从vvv开始跑DFSDFSDFS树(首先把之前的匹配加进DFSDFSDFS树中),对于右边集合相连的两个点就在原树上连一条边。如果过程中途停止的话,当且仅当,对于所有左侧经过的点,与它们相邻的右边的点都经历过了,并且没有扩展出新点。

因为此时左边点多一个vvv,所以左边点数比右边点数多111,那么取右边边未经过的点, 它们在左侧对应的点也是未被经过的,那么f(S)=∣S∣f(S)=|S|f(S)=∣S∣,矛盾。

比较复杂。感性理解一下就好。

请添加图片描述

复杂度O(nn)O(n\sqrt{n})O(nn​) 。瓶颈在于dinic\text{dinic}dinic求最大匹配。

#include
#define ll long long
#define pb push_back
#define fi first
#define se second
#define db double
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
const int N=2e6+5;
int n,m,vis[N],head[N],nxt[N],to[N],cur[N],lab[N],cap[N],tot=1,v;
int U[200005],V[200005];
queueQ;
void add(int u,int v,int w){to[++tot]=v,cap[tot]=w,nxt[tot]=head[u],head[u]=tot;to[++tot]=u,cap[tot]=0,nxt[tot]=head[v],head[v]=tot;
}
bool BFS(){for(int i=0;i<2*n;i++)lab[i]=-1;Q.push(2*n);while(Q.size()){int u=Q.front();Q.pop();for(int k=head[u];k;k=nxt[k]){int v=to[k];if(lab[v]==-1&&cap[k^1]>0)lab[v]=lab[u]+1,Q.push(v);}}return lab[0]!=-1;
}
int flow(int u,int lim){if(u==2*n)return lim;int used=0;for(int k=cur[u];k;k=nxt[k]){cur[u]=nxt[k];int v=to[k];if(lab[u]==lab[v]+1&&cap[k]){int tmp=min(lim-used,cap[k]);int ret=flow(v,tmp);cap[k]-=ret,cap[k^1]+=ret,used+=ret;if(used==lim)break;}}return used;
}
int dinic(){int tot=0;while(BFS()){for(int i=0;i<=2*n;i++)cur[i]=head[i];tot+=flow(0,0x3f3f3f3f);}return tot;
}
void dfs(int u){for(int k=head[u];k;k=nxt[k]){if(to[k]==0||vis[to[k]-n])continue;int id=to[k]-n;vis[id]=1;for(int l=head[to[k]];l;l=nxt[l]){if(cap[l])m++,U[id]=u,V[id]=to[l],dfs(to[l]);}}
}
signed main(){ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);cin>>n;for(int i=1;i<=n;i++)add(0,i,1);for(int i=n+1;i<2*n;i++)add(i,2*n,1);for(int i=1;iint k;cin>>k;while(k--){int x;cin>>x,add(x,n+i,1);}}if(dinic()!=n-1)cout<<"-1";else{for(int k=head[0];k;k=nxt[k]){if(cap[k])v=to[k];}dfs(v);if(m!=n-1)cout<<"-1";else for(int i=1;i

AGC035

Add and Remove

相关内容

热门资讯

银河麒麟V10SP1高级服务器... 银河麒麟高级服务器操作系统简介: 银河麒麟高级服务器操作系统V10是针对企业级关键业务...
【NI Multisim 14...   目录 序言 一、工具栏 🍊1.“标准”工具栏 🍊 2.视图工具...
AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
Android|无法访问或保存... 这个问题可能是由于权限设置不正确导致的。您需要在应用程序清单文件中添加以下代码来请求适当的权限:此外...
月入8000+的steam搬砖... 大家好,我是阿阳 今天要给大家介绍的是 steam 游戏搬砖项目,目前...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...