大体状况
172/300
状态十分差劲= =
T1 multiple
分析
二分然后计算数量然后求一个最大值。
理论复杂度为
O(Tmlog221018)
。
然后那个评测机果然没有跑过去,
只有62分
3分是1计算为0导致的
(在网站上都能过啊
代码
#include<bits/stdc++.h>
using namespace std;
#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmin(a,b) a=min(a,b)
#define chkmax(a,b) a=max(a,b)
#define LL long long
void Rd(int &res){
char c;res=0;
while((c=getchar())<48);
do res=(res<<3)+(res<<1)+(c^48);
while((c=getchar())>47);
}
void Rd(LL &res){
char c;res=0;
while((c=getchar())<48);
do res=(res<<3)+(res<<1)+(c^48);
while((c=getchar())>47);
}
#define M 104
int Pri[M]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541};
int T,m;
LL A[M],Ans;
LL Num[M][M];
void Init(){
REP(i,0,100){
LL v=1;
Num[i][0]=1;
REP(j,1,74)
v*=Pri[i],Num[i][j]=Num[i][j-1]+v;
}
}
LL Calc(int i,int p,LL x){
LL Res=0;int len=0;x/=p;
while(x) Res+=x%p*Num[i][len++],x/=p;
return Res;
}
void Solve(){
Ans=1;
DREP(i,m-1,-1){
if(Calc(i,Pri[i],Ans)>=A[i])continue;
LL l=Ans,r=1000000000000000000ll,Res=0;
while(l<=r){
LL Mid=l+r>>1;
if(Calc(i,Pri[i],Mid)>=A[i])r=Mid-1,Res=Mid;
else l=Mid+1;
}
chkmax(Ans,Res);
}
printf("%lld\n",Ans);
}
int main(){
Init();
Rd(T);
while(T--){
Rd(m);
REP(i,0,m)Rd(A[i]);
Solve();
}
return 0;
}
T2 bracket
分析
P60
朴素的DP。
P100
贪心。
令所有字符视为?,把其转换为另一个括号的代价设为INF。
累计所有转换为右括号的代价和,
然后令转换为左括号的代价减去其转换为右括号的代价和。
这样就变成求合法的n/2个左括号的代价和最小。
然后弄一个优先队列,发现在奇数位取值是肯定可以合法的。
代码
这样并没有把已有的左右括号特殊处理的方法跑得快。
当然复杂度是一样的。
#include<bits/stdc++.h>
using namespace std;
#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define LL long long
void Rd(int &res){
char c;res=0;
while((c=getchar())<48);
do res=(res<<3)+(res<<1)+(c^48);
while((c=getchar())>47);
}
#define M 200004
#define INF 2000000044
char C[M];
int n,X[M],Y[M];
LL Ans;
priority_queue<int>Q;
int main(){
scanf("%s",C);
n=strlen(C);
REP(i,0,n){
if(C[i]=='?')Rd(X[i]),Rd(Y[i]);
else if(C[i]==')')X[i]=INF;
else Y[i]=INF;
Ans+=Y[i],X[i]-=Y[i];
}
REP(i,0,n){
Q.push(-X[i]);
if(!(i&1)) Ans-=Q.top(),Q.pop();
}
printf("%lld\n",Ans);
return 0;
}
T3 trennen
P50(?80)
贪心的这个策略比较显然。
必然从右侧开始按开关,
右侧的灯只有自己这个开关与更右侧是其倍数的开关能改变其状态。
如果按更右侧的开关来关这盏灯,最后还要按一次那个更右侧的开关。
而该开关的约数真包含于更右侧的开关的约数,
因此按那个开关没有意义。
也即,确定了序列后开关的次数必然小于等于n,
可以用上述策略得到最小次数。
对于
k==n
的情况,就可以直接输出这个步数乘上n!。
实际上给了80分。
P100
通过上面那个策略以及这个游戏的本质可以发现:
操作顺序对操作结果没有影响。
所以当在需要操作
t
次的情况中,一定有
然后这就是一个简单的概率DP了。
定义
Fi
为状态
i
到状态
有
Fn=1
;
Fi=tn+n−tn(Fi+Fi+1+1)
后一项为先转化为状态
i+1
然后通过F变回来= =。
整理可得
Fi=n+(n−i)Fi+1i
然后就可以递推了,答案为
∑nowi=k+1
,
now为初始状态所需步数。
整个题目就是这个叫B君的zz不会贪心去随机的结果= =
代码
#include<bits/stdc++.h>
using namespace std;
#define Komachi is retarded
#define REP(i,a,b) for(int i=(a),i##_end_=(b);i<i##_end_;i++)
#define DREP(i,a,b) for(int i=(a),i##_end_=(b);i>i##_end_;i--)
#define chkmin(a,b) a=min(a,b)
#define chkmax(a,b) a=max(a,b)
#define LL long long
void Rd(int &res){
char c;res=0;
while((c=getchar())<48);
do res=(res<<3)+(res<<1)+(c^48);
while((c=getchar())>47);
}
#define M 100004
#define Mod 100003
int n,k,A[M];
int Solve(){
int Ans=0;
DREP(i,n,0){
for(int j=i+i;j<=n;j+=i)if(A[j])A[i]^=1;
Ans+=A[i];
}
return Ans;
}
int Pow(int x,int p){
int Res=1,k=x;
while(p){
if(p&1)Res=1ll*Res*k%Mod;
k=1ll*k*k%Mod;p>>=1;
}
return Res;
}
int Fac(int x){
REP(i,2,n+1)
x=1ll*x*i%Mod;
return x;
}
int main(){
Rd(n),Rd(k);
REP(i,1,n+1)Rd(A[i]);
int Tmp=Solve();
if(Tmp<=k)printf("%d\n",Fac(Tmp));
else{
int Res=1,Ans=0;
DREP(i,n-1,k){
Res=1ll*(n+1ll*Res*(n-i)%Mod)*Pow(i,Mod-2)%Mod;
if(i<=Tmp)(Ans+=Res)%=Mod;
}
Ans+=k;
printf("%d\n",Fac(Ans));
}
return 0;
}
总结
概率DP还是并不怎么会,一看见期望值就懵逼。
这种DP的一个明显特征就是以普通状态转移之间的差值作为DP值。
将二维需要高斯消元的状态关系化为一维比较麻烦。
然后T2的贪心,应该算比较明显了但是没有想到。