牛牛想起飞
n<=1e5,y<=100,可以用dp,定义
d
p
[
i
]
[
j
]
=
{
1
,
变换后前n项和可以为j
0
,
else
dp[i][j]= \begin{cases} 1, &\text{变换后前n项和可以为j} \\0, & \text{else} \end{cases}
dp[i][j]={1,0,变换后前n项和可以为jelse
就可以得到状态转移方程
d
p
[
i
]
[
j
]
=
d
p
[
i
−
1
]
[
(
j
+
a
[
i
−
1
]
)
%
y
]
∣
∣
d
p
[
i
−
1
]
[
(
(
j
−
a
[
i
−
1
]
)
%
y
+
y
)
%
y
]
∣
∣
d
p
[
i
−
1
]
[
a
[
i
−
1
]
%
y
]
dp[i][j]=dp[i-1][(j+a[i-1])\%y]||dp[i-1][((j-a[i-1])\%y+y)\%y]||dp[i-1][a[i-1]\%y]
dp[i][j]=dp[i−1][(j+a[i−1])%y]∣∣dp[i−1][((j−a[i−1])%y+y)%y]∣∣dp[i−1][a[i−1]%y]
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define per(i,s,t) for(int i=s;i>=t;i--)
#define ms0(ar) memset(ar,0,sizeof ar)
#define ms1(ar) memset(ar,-1,sizeof ar)
#define Ri(x) scanf("%d",&x)
#define Rii(x,y) scanf("%d%d",&x,&y)
const int N=1e5+5;
int dp[N][105],a[N],b[N],n,y;
int f(int x){
while(x<0) x+=y;
return x;
}
int main(){
Rii(n,y);
rep(i,1,n) Ri(a[i]);
rep(i,1,n) Ri(b[i]);
dp[0][0]=1;
rep(i,1,n)
rep(j,0,y-1)
if(dp[i-1][j]){
dp[i][f(j+a[i])%y]=1;
dp[i][f(j+a[i]-b[i])%y]=1;
dp[i][f(j+a[i]+b[i])%y]=1;
}
per(i,y-1,0)
if(dp[n][i]){
printf("%d",i);
return 0;
}
}
最小互质数
首先1如果没出现过,答案必然是1
如果1出现过,考虑出现过的数x,若(x,y)=1,则x所有的素因子都不是y的因子,即只要x出现过,x的素因子的倍数都不满足条件
再考虑满足条件的数y,y的素因子一定不是出现过的任何一个数的素因子,那么最小的y必然是一个素数,又考虑到1e5+3是素数,那么结果肯定不会超过1e5+3,于是对[1,1e5+3]先筛一次素数,再对出现的数质因数分解即可
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define per(i,s,t) for(int i=s;i>=t;i--)
#define ms0(ar) memset(ar,0,sizeof ar)
#define ms1(ar) memset(ar,-1,sizeof ar)
#define Ri(x) scanf("%d",&x)
#define Rii(x,y) scanf("%d%d",&x,&y)
const int N=1e5+5;
int cnt=0;
bool r[N],u[N];
ll ans=1;
bool s(int x){
int k=sqrt(x)+1;
rep(i,2,k)
if(!(x%i)) return 0;
return 1;
}
int main(){
int n,k=sqrt(N)+1;
Ri(n);
rep(i,2,k)//筛素数
if(!u[i])
for(int j=i*i;j<N;j+=i)
u[j]=1;
rep(i,1,n){
Ri(k);
if(k==1) r[1]=1;
int t=2;
if(!u[k]){//剪枝 素数不进行质因数分解
r[k]=1;
continue;
}
while(k>1){//质因数分解
if(!(k%t)){
r[t]=1;
while(!(k%t)) k/=t;
}
t++;
}
}
rep(i,1,N)//枚举
if(!r[i]&&!u[i]){
printf("%d",i);
return 0;
}
}
衔尾蛇
三种颜色出现的次数都枚举过去,枚举时用dfs求出所有排列之后,用最小表示法得出该排列的最小表示(取字符串第1~i位放到字符串末尾,字典序最小时即为最小表示),一个环的最小表示必然是相同的,再用set去重即可
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define per(i,s,t) for(int i=s;i>=t;i--)
#define ms0(ar) memset(ar,0,sizeof ar)
#define ms1(ar) memset(ar,-1,sizeof ar)
#define Ri(x) scanf("%d",&x)
#define Rii(x,y) scanf("%d%d",&x,&y)
set<string>g;
void gm(string s){
int i=0,j=1,k=0,l=s.size();
while(i<l&&j<l&&k<l){
int t=s[(i+k)%l]-s[(j+k)%l];
if(!t) k++;
else{
if(t>0) j+=k+1;
else i+=k+1;
if(i==j) j++;
k=0;
}
}
string tmp=s.substr(min(i,j),l-min(i,j))+s.substr(0,min(i,j));
g.insert(tmp);
}
void dfs(int a,int b,int c,string t){
if(!a&&!b&&!c){
if(t!="") gm(t);
return;
}
if(a) dfs(a-1,b,c,t+"a");
if(b) dfs(a,b-1,c,t+"b");
if(c) dfs(a,b,c-1,t+"c");
}
int main(){
int a,b,c;
Rii(a,b);Ri(c);
rep(i,0,a)
rep(j,0,b)
rep(k,0,c)
dfs(i,j,k,"");
printf("%d",g.size());
return 0;
}
牛牛的反函数
首先观察递推式,对输入的y,可以理解成进行递推的次数,而要求输出的x在[1,1e18]之内,则隐含的条件即为要找到最小的x满足F(x)=y
即在给定的递推次数下,找到最小的x
很显然进行x+1的操作会比x/2的操作更能得到最小的x,则递推式一定是先+1再/2一直往复操作
我们考虑一个数列
a
1
=
x
,
a
n
=
(
a
n
−
1
+
1
)
/
2
a_1=x,a_n=(a_{n-1}+1)/2
a1=x,an=(an−1+1)/2,终止时
a
n
=
2
a_n=2
an=2(因为最后两次操作是+1再/2,不可能得到1),可以得到
2
(
a
n
−
1
)
=
a
n
−
1
−
1
2(a_n-1)=a_{n-1}-1
2(an−1)=an−1−1,整理得
a
1
−
1
=
2
n
−
1
(
a
n
−
1
)
=
2
n
−
1
a_1-1=2^{n-1}(a_n-1)=2^{n-1}
a1−1=2n−1(an−1)=2n−1,那么y为偶数时就可以得到
x
=
2
y
/
2
−
1
+
1
x=2^{y/2-1}+1
x=2y/2−1+1
再考虑y为奇数时,就在开始再加上一次/2操作,补上开始的/2操作,得到
x
=
(
2
(
y
−
1
)
/
2
−
1
+
1
)
∗
2
=
2
(
y
−
1
)
/
2
+
2
x=(2^{(y-1)/2-1}+1)*2=2^{(y-1)/2}+2
x=(2(y−1)/2−1+1)∗2=2(y−1)/2+2
然后很容易得到(系统自带的计算器算一下)x<=120
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define per(i,s,t) for(int i=s;i>=t;i--)
#define ms0(ar) memset(ar,0,sizeof ar)
#define ms1(ar) memset(ar,-1,sizeof ar)
#define Ri(x) scanf("%d",&x)
#define Rii(x,y) scanf("%d%d",&x,&y)
int main(){
int t,y;
ll a[130]={0,1};
rep(i,2,121)
if(i&1) a[i]=(1ll<<(i/2))+2;
else a[i]=(1ll<<((i-2)/2))+1;
Ri(t);
while(t--){
Ri(y);
if(y>=121) puts("-1");
else printf("%lld\n",a[y]);
}
return 0;
}
开心消消乐
先考虑能消除的连续区间,一定是形如111000的形式,那么令dp[i].l记录前k个不删除时消除后的长度,dp[i].q记录前k个不删除时消除后末尾连续1的个数.令pd[i].l记录后k个不删除时消除后的长度,dp[i].q记录后k个不删除时消除后前端连续0的个数.
利用dp[i].p来记录前k个不删除时消除之后字符串最后一个字符在原字符串中的位置,那么就可以得到状态转移方程
d
p
[
i
]
.
l
=
{
d
p
[
i
−
1
]
.
l
−
1
,
dp[i-1].p与i可消除
d
p
[
i
−
1
]
.
l
+
1
,
不可消除
dp[i].l= \begin{cases} dp[i-1].l-1,&\text{dp[i-1].p与i可消除} \\dp[i-1].l+1, &\text{不可消除} \end{cases}
dp[i].l={dp[i−1].l−1,dp[i−1].l+1,dp[i-1].p与i可消除不可消除
d
p
[
i
]
.
p
=
{
d
p
[
d
p
[
i
−
1
]
.
p
−
1
]
.
p
−
1
,
可消除
i
,
不可消除
dp[i].p= \begin{cases} dp[dp[i-1].p-1].p-1,&\text{可消除} \\i, &\text{不可消除} \end{cases}
dp[i].p={dp[dp[i−1].p−1].p−1,i,可消除不可消除
这里可消除的情况的式子,是因为dp[i].p也被消除了,所以dp[i].p应该是再往前的未被消除的字符的位置
d
p
[
i
]
.
q
=
{
d
p
[
i
−
1
]
.
q
−
1
,
可消除
d
p
[
i
−
1
]
.
q
+
1
−
s
[
i
]
+
′
0
′
,
不可消除
dp[i].q= \begin{cases} dp[i-1].q-1,&\text{可消除} \\dp[i-1].q+1-s[i]+'0', &\text{不可消除} \end{cases}
dp[i].q={dp[i−1].q−1,dp[i−1].q+1−s[i]+′0′,可消除不可消除
这里不可消除的情况的式子,是s[i]='1’时会加上,否则不加
以及消除后所有0都在最前端,所以一旦开始计数,消除后的字符串后面就只有1,故不用考虑会有1110111这样的情况需要重新计数
pd数组同理也可以得到类似的状态转移方程
那么就可以枚举区间起点终点,若[i+1,j-1]区间满足区间删除的条件时,很容易得到
a
n
s
=
m
i
n
(
d
p
[
i
]
.
l
+
p
d
[
j
]
.
l
−
2
∗
m
i
n
(
d
p
[
i
]
.
q
,
p
d
[
j
]
.
q
)
,
a
n
s
)
ans=min(dp[i].l+pd[j].l-2*min(dp[i].q,pd[j].q),ans)
ans=min(dp[i].l+pd[j].l−2∗min(dp[i].q,pd[j].q),ans)
以及记得ans初始化为没有区间删除操作的情况,即dp[n].l
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define per(i,s,t) for(int i=s;i>=t;i--)
#define ms0(ar) memset(ar,0,sizeof ar)
#define ms1(ar) memset(ar,-1,sizeof ar)
#define Ri(x) scanf("%d",&x)
#define Rii(x,y) scanf("%d%d",&x,&y)
const int N=1e4+5;
int n,a[2][N];
struct node{
int p,q,l;
}dp[N],pd[N];
char s[N];
int main(){
Ri(n);
scanf("%s",s+1);
s[0]=s[n+1]='@';
rep(i,1,n){
if(s[i]=='1'&&s[dp[i-1].p]=='0') dp[i].l=dp[i-1].l-1,dp[i].p=dp[dp[i-1].p-1].p,dp[i].q=dp[i-1].q-1;
else dp[i].l=dp[i-1].l+1,dp[i].p=i,dp[i].q=dp[i-1].q+1-s[i]+'0';
a[s[i]-'0'][i]=a[s[i]-'0'][i-1]+1;
a[1-s[i]+'0'][i]=a[1-s[i]+'0'][i-1];
}
per(i,n,1)
if(s[i]=='0'&&s[pd[i+1].p]=='1') pd[i].l=pd[i+1].l-1,pd[i].p=pd[pd[i+1].p+1].p,pd[i].q=pd[i+1].q-1;
else pd[i].l=pd[i+1].l+1,pd[i].p=i,pd[i].q=pd[i+1].q+s[i]-'0';
int ans=dp[n].l;
rep(i,0,n)
rep(j,i+1,n+1)
if((a[0][j-1]-a[0][i])&&(a[1][j-1]-a[1][i])==(a[0][j-1]-a[0][i])<<1){
ans=min(ans,dp[i].l+pd[j].l-2*min(dp[i].q,pd[j].q));
}
printf("%d",ans);
return 0;
}
CoolCool的序列
很容易想到实际花费只与每个数需要移动的距离有关
考虑三个相同的数按位置从小到大为
x
1
,
x
2
,
x
3
x_1,x_2,x_3
x1,x2,x3
那么翻转后的位置从小到大为
n
−
x
3
+
1
,
n
−
x
2
+
1
,
n
−
x
1
+
1
n-x_3+1,n-x_2+1,n-x_1+1
n−x3+1,n−x2+1,n−x1+1
翻转后如何交换到
x
1
,
x
2
,
x
3
x_1,x_2,x_3
x1,x2,x3的花费最小呢?很显然就是第k小的配对第k小的
设期望移动到的位置为
x
1
,
x
2
,
.
.
.
,
x
n
x_1,x_2,...,x_n
x1,x2,...,xn,那么使交换的总距离尽可能短,就每次都在原序列中找满足
x
i
≥
j
&
x
j
≤
i
x_i≥j\&x_j≤i
xi≥j&xj≤i的两个数交换,那么花费j-i,就可以使总共需要交换的距离减少2(j-i),因此只需要
(
Σ
x
n
)
/
2
(Σx_n)/2
(Σxn)/2
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define per(i,s,t) for(int i=s;i>=t;i--)
#define ms0(ar) memset(ar,0,sizeof ar)
#define ms1(ar) memset(ar,-1,sizeof ar)
#define Ri(x) scanf("%d",&x)
#define Rii(x,y) scanf("%d%d",&x,&y)
const int N=1e5+5;
int a[N],n;
queue<int>b[N];
int main(){
Ri(n);
rep(i,1,n){
Ri(a[i]);
b[a[i]].push(i);
}
ll ans=0;
per(i,n,1){
ans+=abs(b[a[i]].front()-n+i-1);
b[a[i]].pop();
}
printf("%lld",ans>>1);
return 0;
}
牛清楚的裙子!!!
概率期望题,每种裙子出现次数必然是相同的,推出出现次数的期望后即可得到答案
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define per(i,s,t) for(int i=s;i>=t;i--)
#define ms0(ar) memset(ar,0,sizeof ar)
#define ms1(ar) memset(ar,-1,sizeof ar)
#define Ri(x) scanf("%d",&x)
#define Rii(x,y) scanf("%d%d",&x,&y)
const int N=1e7+5;
double f[N];
int main()
{
rep(i,1,N-5) f[i]=f[i-1]+1.0/i;
int t;
Ri(t);
while(t--){
int n;
scanf("%d",&n);
double ans=(n+9999)*f[n];
printf("%.7f\n",ans);
}
return 0;
}