D1T1
Description
给定一个01串,现在要取出一些数且他们两两不相邻,请求出最大的和。
Solution
Subtask 1
枚举每个位置是否被选即可,每次判断是否存在相邻的位置,如果不存在则用和来更新答案。
时间复杂度:
Subtask 2
留给使用 算法的选手。
Subtask 3
做法1
考虑 。
状态设计 表示从 到 取不相邻的数所能得到的最大和。
显然,状态转移方程为 。其中, 表示,该位置的数要取且状态从前两个位置( )转移而来;而 表示,该位置的数不选且状态从上一个位置转移而来。
最终答案就是 。
做法2
考虑,取 对答案并没有贡献,所以我们不取 。
对于每一段连续的 ,假设它的长度为 ,那么顶多能够取 个数。其中 表示 向上取整。
于是,我们直接线性枚举全为 的区间,那么答案就是 。
综上所述,这两种做法的时间复杂度均为 。注意后一种做法的常数更小。
Code
做法1:
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,ans=-1;
int a[1000005],dp[1000005];
signed main()
{
cin>>n;
for (int i=1;i<=n;i++)
{
char x;
cin>>x;
a[i]=x-'0';
}
dp[1]=a[1];
for (int i=2;i<=n;i++) dp[i]=max(dp[i-2]+a[i],dp[i-1]);
for (int i=1;i<=n;i++) ans=max(ans,dp[i]);
cout<<ans<<endl;
return 0;
}//by ducati
做法2:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,i,ans=0,tot=0;
string s;
cin>>n>>s;
for(i=0;i<n;i++)
if(s[i]=='0')
{
ans+=(tot+1)>>1;
tot=0;
}
else
tot++;
cout<<ans+((tot+1)>>1);
return 0;
}//by b6e0
D1T2
Description
请构造出 个数,使得它们与 个运算符相连后得到的答案为 。
Solution
Task 1
可以发现,一定存在答案使得每个数均不大于 且运算后答案为 。
所以,我们枚举每个位置的值,然后暴力计算即可;如果符合,就直接打印输出。
时间复杂度 。
Task 2-10
正解的做法显然,先在第一个位置填上 ,后面的所有数保持运算结果不变即可。
详细地说,我们这样:
①在第一个位置填上
;
②接着填下去,如果
(1)前一个运算符为
,则填0;
(2)前一个运算符为
,则填1;
(3)前一个运算符为
,则填0;
(4)前一个运算符为
,则填0;
(5)前一个运算符为^,则填0;
(6)前一个运算符为&,注意此时每个二进制位都是1才能行,所以应该填
。
注意可以用 语句。
时间复杂度: 。
Code
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,m,i;
char c;
scanf("%d%d",&n,&m);
printf("%d ",m);
for(i=1;i<n;i++)
{
cin>>c;
switch(c)
{
case '+':
printf("0 ");
break;
case '-':
printf("0 ");
break;
case '*':
printf("1 ");
break;
case '&':
printf("2147483647 ");
break;
case '|':
printf("0 ");
break;
case '^':
printf("0 ");
break;
}
}
return 0;
}//by b6e0
D1T3
Description
请求出存在多少个长度为 的字符串,使得每位均为A或B或C或D,并且经过有限次的交换后无法得到连续的 。
Solution
Subtask 1
首先,容易发现,如果暴力交换+暴力判断,本Subtask无法通过。
于是,我们发现,如果字符串中同时存在A,B,C,那么一定能够通过有限次交换得到连续的 。
所以,我们直接暴力枚举每个位置上的字母,然后判断是否同时存在 , , 即可。如果不同时存在,那么答案加上1。
时间复杂度: 。
Subtask 2
考虑 。
状态设计 。 表示目前看到了第 位, 目前是否存在 , 表示目前是否存在 , 表示目前是否存在 , 表示目前是否存在 。
显然,如果在该位置填上 ,那么应该从上一个位置中,所有 或 的状态转移而来,如果该位置填上 ,那么应该从上一个位置中,所有 或 的状态转移而来,填 同理。注意填 的话要继承所有使得 或 或 的状态。
显然,最终答案就是,所有看到第 位,且 或 或 的状态之和。
注意取模。时间复杂度 。
虽然常数很大,但是能够通过。
Subtask 3
通过基本的容斥原理,发现不同时含 , , 的串数量为:
{A,B,D}+{A,C,D}+{B,C,D}-{A,D}-{B,D}-{C,D}+{D}。
其中,{A,B,D}表示所有位为 或 或 的字符串的数量,{D}表示所有位均为 的字符串的数量 。
综上所述,答案就是
注意用快速幂进行优化。
时间复杂度 。
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,mod;
int quick_power(int a,int b)
{
if (b==0) return 1;
int res=1;
for (;b;b=b>>1,a=(a*a))
{
if (b&1) res=(res*a);
}
return res;
}
signed main()
{
cin>>n>>mod;
cout<<((quick_power(3,n+1)-3*quick_power(2,n)+1)%mod+mod)%mod<<endl;
return 0;
}//by ducati
D1T4
Description
给定一个迷宫,每个位置都指向另一个位置,求出从该位置走 步后的位置。
Subtask 1
容易发现,从该位置出发后会形成长度为1的周期。
即,如果该位置指向自己,那么经过 步后他仍然在这里;否则,他会在第一步到另一个位置,并且在那里不动。
Subtask 2
直接模拟即可。
时间复杂度: 。
Subtask 3-4
做法1
这是一道很经典的周期问题。
我们可以直接记录下上一次是什么时候到这个位置,如果现在又到达了这个位置,那么就能轻松得到周期的开始时间与周期的长度。
然后运用周期,将 变小,再查表得出答案即可。
时间复杂度: 。注意这是上限,事实上运行的时间(如果不故意卡的话)会远小于这个。
做法2
考虑倍增优化 。
状态设计 ,表示从位置 走 步的位置——若 则记录下走 步后的横坐标,否则( )记录下走 步后的纵坐标。
状态转移显然,由于这是倍增优化 ,就是 。
但是, 不一定为 的次方数。我们运用"跳"的思想,每次跳 的次方数步。例如,当 时,我们先跳 步,再跳 步。
于是就能很快地得到答案啦。时间复杂度 。
Code
做法1
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,m,si,sj,k,l,r,step=0;
int visited[105][105];
struct node
{
int ii;
int jj;
}a[105][105];
struct node_ans
{
int ai;
int aj;
}ans[1000005];
signed main()
{
cin>>n>>m>>si>>sj>>k;
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)
{
cin>>a[i][j].ii>>a[i][j].jj;
visited[i][j]=-1;
}
}
visited[si][sj]=0;
ans[0].ai=si,ans[0].aj=sj;
while (1)
{
int posi=a[si][sj].ii,posj=a[si][sj].jj;
si=posi,sj=posj;
step++;
if (visited[si][sj]!=-1)
{
l=visited[si][sj];
r=step-l;
ans[step].ai=si,ans[step].aj=sj;
break;
}
else
{
visited[si][sj]=step;
ans[step].ai=si,ans[step].aj=sj;
}
}
if (k<=l) cout<<ans[k].ai<<' '<<ans[k].aj<<endl;
else
{
k=(k-l)%r+l;
cout<<ans[k].ai<<' '<<ans[k].aj<<endl;
}
return 0;
}//by ducati
做法2
#include<bits/stdc++.h>
using namespace std;
int po[105][105][2],dp[105][105][70][2];
int main()
{
int n,m,a,b,i,j,k,x,y;
long long t,s=1;
cin>>n>>m>>a>>b>>t;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
{
cin>>po[i][j][0]>>po[i][j][1];
dp[i][j][0][0]=po[i][j][0];
dp[i][j][0][1]=po[i][j][1];
}
for(k=1;k<=62;k++)
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
{
dp[i][j][k][0]=dp[dp[i][j][k-1][0]][dp[i][j][k-1][1]][k-1][0];
dp[i][j][k][1]=dp[dp[i][j][k-1][0]][dp[i][j][k-1][1]][k-1][1];
}
for(i=0;i<62;i++)
s*=2;
for(;s;s/=2,i--)
if(s<=t)
{
x=a;
y=b;
a=dp[x][y][i][0];
b=dp[x][y][i][1];
t-=s;
}
cout<<a<<' '<<b;
return 0;
}//by b6e0
撒花✿✿ヽ(°▽°)ノ✿撒花