AtCoder Grand Contest 005

A - STring

翻译

给定一个只包含\(ST\)的字符串,如果出现了连续的\(ST\),就把他删去,然后所有位置前移。问最后剩下的串长。

题解

模拟栈,和维护括号一样的。

#include<iostream>
#include<cstring>
using namespace std;
#define MAX 200200
char ch[MAX];
int ans,tot;
int main()
{
cin>>(ch+1);
for(int i=1,l=strlen(ch+1);i<=l;++i)
if(ch[i]=='S')++tot;
else tot?--tot:++ans;
cout<<ans+tot<<endl;
return 0;
}

B - Minimum Sum

翻译

给定排列\(a_i\)

求\(\sum_{l=1}^n\sum_{r=l}^nmin(a_l,a_{l+1},...,a_r)\)

题解

比较套路的题目,对于每个数维护左右区间第一个比它大的数字,那么当前的这部分区间的最小值都是这个值,直接算贡献即可。

#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define MAX 200200
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n,a[MAX],Q[MAX],top;
int l[MAX],r[MAX];
ll ans;
int main()
{
n=read();
for(int i=1;i<=n;++i)a[i]=read(),l[i]=1,r[i]=n;
top=0;
for(int i=1;i<=n;++i)
{
while(top&&a[Q[top]]>a[i])--top;
if(top)l[i]=Q[top]+1;
Q[++top]=i;
}
top=0;
for(int i=n;i>=1;--i)
{
while(top&&a[Q[top]]>a[i])--top;
if(top)r[i]=Q[top]-1;
Q[++top]=i;
}
for(int i=1;i<=n;++i)ans+=1ll*(i-l[i]+1)*(r[i]-i+1)*a[i];
cout<<ans<<endl;
return 0;
}

C - Tree Restoring

翻译

有一个\(n\)个节点的树,告诉你距离每个点的最大距离,问这样的树是否存在。

题解

一个性质,距离树上任意一点的最远点一定是直径的一个端点。那么先看看直径是否存在,然后看看剩下的点的距离是否都满足大于直径长度的一半,对于直径长度的奇偶性分开考虑一下。细节好烦啊。

#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 111
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n,a[MAX],mx,vis[MAX];
void Out(){puts("Impossible");exit(0);}
int main()
{
n=read();
for(int i=1;i<=n;++i)mx=max(mx,a[i]=read());
for(int i=1;i<=n;++i)vis[a[i]]++;
if(mx&1)
{
for(int i=mx-mx/2;i<=mx;++i)if((vis[i]-=2)<0)Out();
for(int i=mx-mx/2;i;--i)if(vis[i])Out();
}
else
{
if(!vis[mx/2])Out();vis[mx/2]-=1;
for(int i=mx/2+1;i<=mx;++i)if((vis[i]-=2)<0)Out();
for(int i=mx/2;i;--i)if(vis[i])Out();
}
puts("Possible");
return 0;
}

D - ~K Perm Counting

翻译

给定\(n,K\)。求\(1..n\)所有排列中,不存在\(|a_i-i|=K\)的排列个数。

题解

看到题目就可以往容斥的方面靠。求解至少有\(K\)个不合法的方案数,往\(dp\)方面靠。

发现所有可能出现上述情况的数,一定都是模\(K\)意义下相等的数,那么把所有这样的数给扣下来,显然就变成了给你一个两侧都是\(m\)个点的二分图,第\(i\)个点向着\(i-1\)和\(i+1\)连边,求连了\(x\)条边的方案数。这个直接简单\(dp\)算,状压一下下面的相邻的两个点的状态转移就好饿了。然后再把所有的结果全部合并,显然合并的过程互不影响,就是一个背包。

这样求出来的是恰好匹配的方案数,但是剩下的数可以随便放,所以乘阶乘,从恰好变成了至少,那么容斥计算答案即可。

相当好的一道题目。

#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define MOD 924844033
#define MAX 2020
void add(int &x,int y){x+=y;if(x>=MOD)x-=MOD;}
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int g[MAX][MAX][4];
int f[MAX],jc[MAX];
int n,K,m,ans;
int main()
{
n=read();K=read();m=(n-1)/K+1;
jc[0]=1;
for(int i=1;i<=n;++i)jc[i]=1ll*jc[i-1]*i%MOD;
g[1][0][0]=1;g[1][1][2]=1;
for(int i=1;i<m;++i)
for(int j=0;j<=i;++j)
for(int k=0;k<4;++k)
{
if(!g[i][j][k])continue;
add(g[i+1][j][k>>1],g[i][j][k]);
if(!(k&1))add(g[i+1][j+1][k>>1],g[i][j][k]);
add(g[i+1][j+1][(k>>1)|2],g[i][j][k]);
}
f[0]=1;int s=0;
for(int i=1;i<=K;++i)
{
int p=(n-i)/K+1;s+=p;
for(int j=s;j;--j)
for(int k=1;k<=p;++k)
if(j>=k)add(f[j],1ll*f[j-k]*((g[p][k][0]+g[p][k][1])%MOD)%MOD);
}
for(int i=0;i<=n;++i)f[i]=1ll*f[i]*jc[n-i]%MOD;
for(int i=0;i<=n;++i)add(ans,(i&1)?MOD-f[i]:f[i]);
printf("%d\n",ans);
return 0;
}

E - Sugigma: The Showdown

翻译

有一个\(n\)个点的图,有\(n-1\)条红边,\(n-1\)条蓝边,每种颜色的边都连成了一棵树。\(A\)控制一个棋子从\(X\)开始,\(B\)控制一个棋子从\(Y\)开始。每次\(A\)可以不动或者走一条红边,\(B\)可以不动或者走一条蓝边。如果棋子相遇则游戏结束。\(A\)希望最大化游戏时间,\(B\)希望最小化游戏时间,求出游戏进行的时间,如果可以无限循环下去输出\(-1\)。

题解

我只感觉自己有些想法,但是细节什么的有点难考虑啊。(还是题解好)

先考虑如何判断游戏会永远进行下去。只要不出现被抓住的情况就可以进行下去啊。那么什么情况下会被抓住呢?假设当前\(A\)的位置是\(u\),\(B\)的位置是\(v\)。如果\(u\)以及使用红边下与其相邻的所有点在蓝边的情况下都与\(v\)的距离不超过\(1\),那么就原地等死吧。否则,反过来,如果出现了一条边\((u,v)\),满足\(dis\_blue(u,v)\gt 2\),并且\(A\)已经到了\(u\)位置而且\(B\)还没抓住,那么只要\(B\)一靠近,\(A\)只需要沿着这条边往另外一个方向走,\(B\)至少要走两步才能过来,那么\(A\)接着换方向就好了,那么这样就停不下来了。

前面既然用了"否则"这个词,意味着如果不能永远进行下去,那么\(A\)一定会被抓(这不废话吗)。那么显然不会走到任何一条满足在蓝树上两点距离大于\(2\)的边,深思熟虑一波,发现如果不存在这样的边的话,\(A\)打死都走不出\(B\)的子树。那么以\(Y\)为根节点,找到所有\(A\)能够到达的点,显然\(A\)只会前往一个深度最深的点然后在那里等死。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define MAX 200200
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
struct Line{int v,next,col;}e[MAX<<2];
int h[MAX],cnt=1,E[MAX][2];
inline void Add(int u,int v,int col){e[cnt]=(Line){v,h[u],col};h[u]=cnt++;}
int dfn[MAX],tim,fa[MAX],dep[MAX],low[MAX],dis[MAX],X,Y,n;
bool inf[MAX],vis[MAX];
void dfs(int u,int ff)
{
dfn[u]=++tim;fa[u]=ff;dep[u]=dep[ff]+1;
for(int i=h[u];i;i=e[i].next)
if(e[i].col&&e[i].v!=ff)
dfs(e[i].v,u);
low[u]=++tim;
}
bool check(int x,int y)
{
if(dfn[x]>dfn[y])swap(x,y);
if(low[x]>=low[y])return dep[y]-dep[x]>2;
if(fa[x]==fa[y])return false;
return true;
}
void BFS()
{
queue<int> Q;vis[X]=true;Q.push(X);
while(!Q.empty())
{
int u=Q.front();Q.pop();
for(int i=h[u];i;i=e[i].next)
if(!e[i].col&&!vis[e[i].v])
{
dis[e[i].v]=dis[u]+1;
if(dis[e[i].v]<dep[e[i].v])
vis[e[i].v]=true,Q.push(e[i].v);
}
}
}
int main()
{
n=read();X=read();Y=read();
for(int i=1;i<n;++i)E[i][0]=read(),E[i][1]=read();
for(int i=1;i<n;++i)
{
int u=read(),v=read();
Add(u,v,1);Add(v,u,1);
}
dep[0]=-1;dfs(Y,0);
for(int i=1;i<n;++i)
{
int u=E[i][0],v=E[i][1];
if(check(u,v))inf[u]=inf[v]=true;
else Add(u,v,0),Add(v,u,0);
}
BFS();
for(int i=1;i<=n;++i)if(vis[i]&&inf[i]){puts("-1");return 0;}
int ans=0;
for(int i=1;i<=n;++i)if(vis[i])ans=max(ans,dep[i]<<1);
printf("%d\n",ans);
return 0;
}

F - Many Easy Problems

题面

给定一个\(n\)个节点的树,定义\(f(S)\)表示在树上包含点集\(S\)的最小联通块的大小。对于每一个\(k\),求出所有大小为\(k\)的点集的\(f(S)\)的和。模数\(924844033\),原根是\(5\)。

题解

总是觉得自己好\(naive\)啊,碰到题目总是毫无思路怎么办啊?我还有救吗?

我们对于每一个点分开考虑他在什么时候会对于一个点集产生贡献。显然这样子是不好算的,还是正难则反,我们考虑它在什么时候不会对于一个点集产生贡献,假设点集大小为\(K\)。那么当且仅当选定的\(K\)个点都在以当前点为根时的同一子树内。所以当前点对于点集大小为\(K\)时,产生的贡献是\(C_n^k-\sum_{v\in son} C_{size[v]}^k\),其中\(size\)是子树大小。

那么对于每一个\(K\),我们把所有点产生的贡献给加起来,也就是\(nC_n^k-\sum_{i=0}^n num[i]*C_i^k\),其中\(num[i]\)表示大小为\(i\)的子树的个数。发现\(num\)其实很好求,\(dfs\)一遍就完事了。前面那部分是定值,最后再考虑。后面的式子直接把组合数给拆开,显然是卷积的形式,\(NTT\)即可。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define MOD 924844033
#define ll long long
#define MAX 888888
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
struct Line{int v,next;}e[MAX];
int h[MAX],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
int n,f[MAX],g[MAX],size[MAX];
int jc[MAX],jv[MAX],inv[MAX];
void dfs(int u,int ff)
{
size[u]=1;
for(int i=h[u];i;i=e[i].next)
{
int v=e[i].v;if(v==ff)continue;
dfs(v,u);size[u]+=size[v];
++f[size[v]];
}
++f[n-size[u]];
}
int C(int n,int m){if(m>n)return 0;return 1ll*jc[n]*jv[m]%MOD*jv[n-m]%MOD;}
int N,l,r[MAX],W[MAX];
int fpow(int a,int b)
{
int s=1;
while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}
return s;
}
void NTT(int *P,int opt)
{
for(int i=1;i<N;++i)if(i>r[i])swap(P[i],P[r[i]]);
for(int i=1;i<N;i<<=1)
{
int w=fpow(5,(MOD-1)/(i<<1));W[0]=1;
for(int k=1;k<i;++k)W[k]=1ll*W[k-1]*w%MOD;
for(int p=i<<1,j=0;j<N;j+=p)
for(int k=0;k<i;++k)
{
int X=P[j+k],Y=1ll*P[i+j+k]*W[k]%MOD;
P[j+k]=(X+Y)%MOD;P[i+j+k]=(X+MOD-Y)%MOD;
}
}
if(opt==-1)
{
reverse(&P[1],&P[N]);
for(int i=0,inv=fpow(N,MOD-2);i<N;++i)P[i]=1ll*P[i]*inv%MOD;
}
}
int main()
{
n=read();
for(int i=1;i<n;++i)
{
int u=read(),v=read();
Add(u,v);Add(v,u);
}
dfs(1,0);
jc[0]=jv[0]=inv[0]=inv[1]=1;
for(int i=1;i<=n;++i)jc[i]=1ll*jc[i-1]*i%MOD;
for(int i=2;i<=n;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
for(int i=1;i<=n;++i)jv[i]=1ll*jv[i-1]*inv[i]%MOD;
for(N=1;N<n+n;N<<=1)++l;
for(int i=0;i<N;++i)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
for(int i=0;i<=n;++i)f[i]=1ll*f[i]*jc[i]%MOD;
for(int i=0;i<=n;++i)g[i]=jv[n-i];
NTT(f,1);NTT(g,1);
for(int i=0;i<N;++i)f[i]=1ll*f[i]*g[i]%MOD;
NTT(f,-1);
for(int i=1;i<=n;++i)f[i]=1ll*f[i+n]*jv[i]%MOD;
for(int i=1;i<=n;++i)f[i]=(1ll*n*C(n,i)%MOD+MOD-f[i])%MOD;
for(int i=1;i<=n;++i)printf("%d\n",f[i]);
return 0;
}

AtCoder Grand Contest 005的更多相关文章

  1. AtCoder Grand Contest 005 C - Tree Restoring

    题目传送门:https://agc005.contest.atcoder.jp/tasks/agc005_c 题目大意: 给定一个长度为\(N\)的整数序列\(A_i\),问能否构造一个\(N\)个节 ...

  2. AtCoder Grand Contest 005【A栈模拟,B单调栈】

    挖草,AtCoder实在是太吊了~ %%%,目前只A了两题: A题: 就是利用栈模拟一下就好了:S进栈,T的话有S就出栈,然后len减一下就好了: #include <bits/stdc++.h ...

  3. Atcoder Grand Contest 005 E - Sugigma: The Showdown(思维题)

    洛谷题面传送门 & Atcoder 题面传送门 记先手移动棋子的树为红树,后手移动棋子的树为蓝树. 首先考虑一个性质,就是如果与当前红色棋子所在的点相连的边中存在一条边,满足这条边的两个端点在 ...

  4. AtCoder Grand Contest 005题解

    传送门 \(A\) 咕咕 const int N=5e5+5; char s[N];int res,n,sum; int main(){ scanf("%s",s+1),res=n ...

  5. AtCoder Grand Contest 012

    AtCoder Grand Contest 012 A - AtCoder Group Contest 翻译 有\(3n\)个人,每一个人有一个强大值(看我的假翻译),每三个人可以分成一组,一组的强大 ...

  6. AtCoder Grand Contest 011

    AtCoder Grand Contest 011 upd:这篇咕了好久,前面几题是三周以前写的... AtCoder Grand Contest 011 A - Airport Bus 翻译 有\( ...

  7. AtCoder Grand Contest 031 简要题解

    AtCoder Grand Contest 031 Atcoder A - Colorful Subsequence description 求\(s\)中本质不同子序列的个数模\(10^9+7\). ...

  8. AtCoder Grand Contest 010

    AtCoder Grand Contest 010 A - Addition 翻译 黑板上写了\(n\)个正整数,每次会擦去两个奇偶性相同的数,然后把他们的和写会到黑板上,问最终能否只剩下一个数. 题 ...

  9. AtCoder Grand Contest 009

    AtCoder Grand Contest 009 A - Multiple Array 翻译 见洛谷 题解 从后往前考虑. #include<iostream> #include< ...

随机推荐

  1. 执行shell脚本时提示bad interpreter:No such file or directory的解决办法

    执行shell脚本时提示bad interpreter:No such file or directory的解决办法 故障现象:在终端直接cd /var正常,在shell脚本中执行则报错.原因是脚本是 ...

  2. 阿里路由框架ARouter的使用步骤

    ARouter的使用步骤(以宿主APP modulebase和moduleuser 三大模块组成的工程为例) 第一步 因为路由跳转是子模块都需要用到的,所以我们在module_base模块中引入 co ...

  3. SQL Operations Studio的安装和使用

    之前管理和访问SQL SERVER使用的自然是SSMS,功能确实很强大的一个数据库图形化管理软件,但是SSMS有个问题就是体积超级大,启动速度也就比较慢.今天我正好要学习一些T-SQL的内容,在微软的 ...

  4. 关于nodejs中遇到mysql默认8小时连接断开机制的终极简单解决方案

    由于mysql默认8小时连接无访问,就会断开.为此查了一下资料,有同种比较简单的解决方案: 1. 增加 MySQL 的 wait_timeout 属性的值. 修改 /etc/mysql/my.cnf文 ...

  5. 对PBFT算法的理解

    PBFT论文断断续续读了几遍,每次读或多或少都会有新的理解,结合最近的项目代码,对于共识的原理有了更清晰的认识.虽然之前写过一篇整理PBFT论文的博客,但是当时只是知道了怎么做,却不理解为什么.现在整 ...

  6. Android NDK 工具链的使用方法(Standalone Toolchain)

    转载:http://blog.csdn.net/smfwuxiao/article/details/6587709 首先需要确定目标机器的指令集. 如果是 x86 的机器,用 x86-4.4.3 版本 ...

  7. python实现将json数据以json格式写入txt文件

    json.dumps中indent参数是设置json缩进量的 举例: tmp = { "aaa" : "111", "bbb" : '222 ...

  8. spring冲刺计划

    会议召开时间表 日期 时间 内容 05/09 21:00-22:00 讨论题目(未果) 05/10 21:00-21:30 确定题目(网络助手) 05/13 21:00-21:45 讨论软件页面设计 ...

  9. Task2 四则运算2

    1.任务要求:对之前的自动出题系统提出了新的要求:(1).题目避免重复:(2).可定制(数量/打印方式):(3)可以控制下列参数:是否有乘除法.数值范围.加减有无负数.除法有无余数.是否支持分数... ...

  10. echart 插件实现全国地图

    最近的项目要用到一个能展现全国地图的功能,并且全国各个省份显示的颜色不同,点击省份后会返回省份名称.经过反复的查找最终确定了echart这个插件,最后的成果还不错,在这里写下来希望对大家有所帮助.话不 ...