Atcoder arc092

E-Both Sides Merger

给你一个序列,支持两种操作,直到序列中只有一个数时停下来,使得剩下数最大,并输出选数方案。

操作1:扔掉一个最前端或最后端的元素。操作2:选取一个不在边界上的元素,取其相邻两个数的和替换它,并删去它相邻的两个数。

n<=1000,|ai|<=1e9。

结论1:最后留下的数是原序列若干个数的和,并且这些数的下标同奇偶。

证明:一次两边往中间合并的过程等价于右边那个加在左边那个上,并删去中间的和右边的。由此发现整个数列下标的奇偶性都不变。

因为最后选择的两个数下标同奇偶,而这两个是由与它们下标同奇偶的另一些数构成的,因此所有选择的数同奇偶。

结论2:任意一种合法的取数方法,都可以构造成功。

证明:每两个待选数中间有奇数个数,可以一直用操作2使得每两个数相隔一位。再选择中间的元素合并即可。

取奇数/偶数的正数和较大的进行构造。选数方案从后往前选可以使得前面的下标不变,比较好处理。

对整个序列都是负数的特判一下。注意Max要设为负数。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int N=1005;
 4 int n,Max,pos,tot,ans[N],a[N];
 5 vector<int> vec;
 6 typedef long long ll;
 7 ll sum1,sum2;
 8 int main()
 9 {
10    scanf("%d",&n);Max=-1e9-1;//注意max要赋值成负的!
11    for (int i=1;i<=n;i++) 
12    {
13       scanf("%d",&a[i]);if (a[i]>Max) Max=a[i],pos=i;
14       if (i&1) sum1+=(a[i]>0)?a[i]:0;
15       else sum2+=(a[i]>0)?a[i]:0;
16    }
17    if (Max<0) 
18    {
19      printf("%d\n%d\n",Max,n-1);
20      for (int i=n;i>pos;i--) printf("%d\n",i);
21      for (int i=1;i<pos;i++) puts("1");
22      return 0;
23    }
24    for (int i=(sum1>sum2?1:2);i<=n;i+=2) if (a[i]>0) vec.push_back(i);
25    printf("%lld\n",max(sum1,sum2));
26    for (int i=n;i>vec[vec.size()-1];i--) ans[++tot]=i;
27    for (int i=vec.size()-1;i>=1;i--)
28    {
29      int t=(vec[i]-vec[i-1])/2-1;
30      while (t--) ans[++tot]=vec[i-1]+2;
31      ans[++tot]=vec[i-1]+1;
32    }
33    for (int i=1;i<vec[0];i++) ans[++tot]=1;
34    printf("%d\n",tot);
35    for (int i=1;i<=tot;i++) printf("%d\n",ans[i]);
36    return 0;
37 }
View Code

 

F-Two Faces Edges

对于每一条边问反转方向后图中连通块的个数会否改变?n<=1000.

对于一条边u->v来说,考虑两个判定条件:1.v->u是否成立。2.u->v不经过(u,v)是否成立。

当这两个判定有且仅有一个成立时连通块个数会改变。第一个dfs一下。第二个按照邻接表正反dfs一下,并记录到达某个点的前驱,若从u开始的两次dfs中v的前驱有一个不为u,那么判定2成立。

O(n^2)。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int read()
 4 {
 5    int x=0,f=1;char ch=getchar();
 6    while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
 7    while (ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
 8    return x*f;
 9 }
10 const int N=1005;
11 const int M=200005; 
12 vector<int> vec[N];
13 int now,x[M],y[M],n,m,vis1[N][N],vis2[N][N];
14 void dfs1(int x,int fa)
15 {
16     for (int i=0;i<vec[x].size();i++)
17       if (vec[x][i]!=fa)
18            if (!vis1[now][vec[x][i]]) vis1[now][vec[x][i]]=x,dfs1(vec[x][i],x);
19 }
20 void dfs2(int x,int fa)
21 {
22     for (int i=vec[x].size()-1;i>=0;i--)
23       if (vec[x][i]!=fa)
24            if (!vis2[now][vec[x][i]]) vis2[now][vec[x][i]]=x,dfs2(vec[x][i],x);
25 }
26 int main()
27 {
28     n=read();m=read();
29     for (int i=1;i<=m;i++) x[i]=read(),y[i]=read(),vec[x[i]].push_back(y[i]);
30     for (int i=1;i<=n;i++) 
31     { 
32       now=i;vis1[now][now]=vis2[now][now]=1;//注意把起点的vis标记! 
33       dfs1(i,-1);
34       dfs2(i,-1);
35     }
36     for (int i=1;i<=m;i++)
37         puts(((vis1[y[i]][x[i]]!=0)^(vis1[x[i]][y[i]]!=x[i]||vis2[x[i]][y[i]]!=x[i]))?"diff":"same");
38    return 0;
39 }
View Code

 

转载于:https://www.cnblogs.com/Scx117/p/9169253.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ARC069 D 题目传送门:https://atcoder.jp/contests/arc069/tasks/arc069_b 题目描述: 给定两个长度为 $n$ 的字符串 $s$ 和 $t$,每个字符都是小写字母。你需要找到一个长度为 $n$ 的字符串 $u$,满足: - 对于所有 $i \in [1,n]$,都有 $u_i \in \{s_i,t_i\}$。 - 不存在相邻的字符 $u_i$ 和 $u_{i+1}$ 相等。 求满足条件的字符串 $u$ 的个数,对 $10^9+7$ 取模。 解题思路: 这是一道比较经典的字符串构造问题,可以用 dp 或者数学方法来解决。 方法一:dp 我们可以使用 dp 来解决这个问题。设 $f_{i,j}$ 表示构造了前 $i$ 个字符,第 $i$ 个字符为 $j$,且第 $i-1$ 个字符为 $s_{i-1}$ 或 $t_{i-1}$ 的方案数。其中,$j \in \{s_i,t_i\}$。 状态转移方程如下: $$f_{i,j} = \begin{cases} 1, & i=1, j=s_1 \text{ 或 } j=t_1\\ 0, & i=1, j \neq s_1 \text{ 且 } j \neq t_1\\ \sum\limits_{k \in \{s_{i-1},t_{i-1}\},k \neq j} f_{i-1,k}, & i>1 \end{cases}$$ 最终的答案为 $f_{n,s_n}+f_{n,t_n}$。 时间复杂度为 $O(n)$。 方法二:数学 我们可以发现,对于任意一个长度为 $n$ 的字符串 $s$ 和 $t$,它们的方案数都是 $2^n$。因为对于每个位置,我们都可以选择 $s$ 或 $t$ 中的任意一个字符。 但是,这样计算出来的方案数显然是错误的,因为存在相邻字符相等的限制条件。 考虑将所有满足限制条件的字符串按照第一个字符 $j$ 分类,其中 $j \in \{s_1,t_1\}$。这样,整个字符串集合就被划分为了 $4$ 个子集: - $S_1$:以 $s_1$ 开头,且不存在相邻字符相等的字符串。 - $S_2$:以 $s_1$ 开头,且存在相邻字符相等的字符串。 - $S_3$:以 $t_1$ 开头,且不存在相邻字符相等的字符串。 - $S_4$:以 $t_1$ 开头,且存在相邻字符相等的字符串。 我们需要计算的就是第 $n$ 个位置的字符为 $s_n$ 或 $t_n$,且不存在相邻字符相等的字符串的方案数。 对于 $S_1$,第 $i$ 个字符可以选择 $s_i$ 或 $t_i$,但是由于相邻字符不能相等,所以第 $i+1$ 个字符必须与第 $i$ 个字符不同,因此第 $i+1$ 个字符只有 $2$ 种选择。 对于 $S_2$,第 $i$ 个字符可以选择 $s_i$ 或 $t_i$,但是由于相邻字符必须相等,所以第 $i+1$ 个字符必须与第 $i$ 个字符相等,因此第 $i+1$ 个字符只有 $1$ 种选择。 对于 $S_3$ 和 $S_4$,与 $S_1$ 和 $S_2$ 类似,我们可以得到第 $i+1$ 个字符的选择个数为 $2$ 和 $1$。 综上所述,我们可以得到递推式: $$\begin{cases} a_{i+1} = 2(b_i+c_i+d_i) \\ b_{i+1} = a_i \\ c_{i+1} = 2(a_i+d_i) \\ d_{i+1} = b_i \end{cases}$$ 初始状态为 $a_1=1,b_1=0,c_1=1,d_1=1$。 最终的答案为 $a_n+c_n$。 时间复杂度为 $O(n)$。 代码实现:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值