这三场比赛复盘感觉题还挺简单的,但是赛场上想不到正确的方法,大概是题做少了。
第一场和第三场都是Rank1100+,第二场也不知道为什么,两题就混进了Rank600+。
复赛准备白给。
纯模拟,取最大值。
#pragma GCC optimize(2)
#include
using namespace std;
const int maxn=110;
int t,n,a[maxn];
double ans,b[maxn];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin>>t;
while(t--)
{
ans=0;
cin>>n;
for(int i=1;i<=n;++i)
{
cin>>a[i]>>b[i];
b[i]=1-b[i];
ans=max(ans,b[i]/(a[i]+b[i]));
}
cout<<fixed<<setprecision(5)<<ans<<endl;
}
}
根据题意,如果拿到大于一元,一定是拿到了2x那一堆,直接不换。如果小于等于一元,设拿到的是n元,是x和2x的几率都是一半,期望为0.5x2n+0.5x0.5n=1.25n。因为期望大于n,所以换。
#pragma GCC optimize(2)
#include
using namespace std;
const int maxn=110;
int t;
double num;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin>>t;
while(t--)
{
cin>>num;
if(num>1)
cout<<"No"<<endl;
else
cout<<"Yes"<<endl;
}
}
我曾经在极度愤怒的状态下因为忘记把freopen注释掉把自己WA得心态爆炸。
对于1…n的序列,当我们交换1与n后,n作为整个序列中最大的元素,与之后的n-1个元素各组成一个逆序对,而1作为最小的序列,与它之前的所有n-1个元素也各形成一个逆序对,但是请注意n与1和1与n的逆序对重复了,所以最终形成了n-2个逆序对。可以想到,只要n一直在第一位,无论后面数字的顺序怎么改变,形成的逆序对数是一定的,1同理。由此我们得到了(n-1)+(n-2)个逆序对和一条去除了1和n的,长度为n-2的序列2…n-1,再对它做上述一模一样的操作,得到n-3个逆序对,n-4个逆序对等等。最终变成了简单的前缀和问题。
#pragma GCC optimize(2)
#include
#define int long long
using namespace std;
const int maxn=1e6+7;
int t,n,m,sum[maxn];
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
for(int i=1;i<maxn;++i)
sum[i]=sum[i-1]+i;
cin>>t;
while(t--)
{
cin>>n>>m;
if(m>(n/2)) m=n/2;
cout<<sum[n-1]-sum[((n-2*m-1)>0) ? (n-2*m-1) : 0]<<endl;
}
}
模拟,在最优情况下右车道的车始终沿着右车道行驶最快,而对于左车道的每一辆车,如果正右后方那个位置没车,就可以在刚过线时变道到右车道,少花费1点时间。根据以上分析,不存在停顿等待的情况,此时可以认为只有每个车道的最后一辆车对整体的结束时间有影响,所以只需要算出他们两的时间,取更大的一个即可。注意不要忘记过滤空车道。
#include "bits/stdc++.h"
using namespace std;
const int maxn=1e5+7;
int cas,n,x,y,l[maxn],r[maxn],lastl,lastr;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin>>cas;
while(cas--)
{
lastl=lastr=0;
memset(l,0,sizeof(l));
memset(r,0,sizeof(r));
cin>>n;
for(int i=1;i<=n;++i)
{
cin>>x>>y;
if(x==1)
r[y]=true,lastr=max(lastr,y);
else
l[y]=true,lastl=max(lastl,y);
}
if(lastr) lastr+=1;
if(lastl)
if(!r[lastl+1]) lastl+=2;
else lastl+=3;
cout<<max(lastl,lastr)<<endl;
}
}
DP,dp[i][j][k]
存储到第i格为止,一共放掉j个传送门,到第i格连续传送门的长度。然后推状态转移方程:如果第i格放传送门,一定是由前一个状态的ijk分别+1得来的,即为dp[i][j][k]=dp[i-1][j-1][k-1]
。如果第i格不放传送门,则可以是由dp[i-1][j]
的任意一个k得到,题面中求的是加和,于是对k从0到10的dp[i-1][j][k]
求和得出(到10的原因是筛子最多掷出11,而且传送门只能后退不能前进,且会门套门,直到回退到一个没有传送门的方块作为落脚点为止,前一个落脚点必定在10格以内,否则就会永远无法到达终点)。观察到有多组数据且状态通用,开局跑完maxn然后O(1)查询。
#include "bits/stdc++.h"
using namespace std;
const int mod=1e9+7;
const int maxn=1001;
int cas,n,m;
long long dp[maxn][maxn][11];
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
n=m=1000;
dp[1][0][0]=1;
for(int i=2;i<=n;++i)
for (int j = 0; j <= min(i - 1, m); ++j)
{
long long tmp = 0;
for (int k = 1; k <= min(j, 10); ++k) {
dp[i][j][k] = dp[i - 1][j - 1][k - 1] * (i - 1);
dp[i][j][k]%=mod;
tmp += dp[i - 1][j][k];
tmp%=mod;
}
dp[i][j][0] = tmp+dp[i-1][j][0];
dp[i][j][0]%=mod;
}
cin>>cas;
while(cas--)
{
cin>>n>>m;
if(!dp[n][m][0])
cout<<"-1"<<endl;
else
cout<<dp[n][m][0]<<endl;
}
}
最开始特判写在了读图前面,导致输入错位MLE了十几发,服了。
根据题意,图为一颗树,且对于任意有边连接的u和v两点,从u到v和从v到u都只能走一遍。如果第一只蚂蚁走到错误的分支上,必然会折返回正确的道路中。第二只蚂蚁但凡走歪就会失败,每个点不失误的概率是1/(该点出边中第一只蚂蚁走过的数目)
,成功的概率为最短路上每个点的概率之积。可以算出,第一只蚂蚁从最短路上走歪的次数为1时,第二只蚂蚁的成功概率为1/2,所以我们只需要算第一只蚂蚁从最短路上最多只会走歪一次的概率。
DFS记录父节点,再从m走到1,沿路计算概率,需要使用逆元,详见注释。
#include "bits/stdc++.h"
using namespace std;
typedef long long ll;
const int maxn=1e5+7;
const int mod=1e9+7;
int cas,n,m,u,v,fa[maxn];
ll a,b;
vector<int> g[maxn];
ll mod_pow(ll x,ll n)
{
ll res=1;
while(n>0)
{
if(n&1) res=res*x%mod;
x=x*x%mod;
n>>=1;
}
return res;
}
ll inv(ll x)
{
return mod_pow(x,mod-2);
}
void dfs(int x,int father)
{
fa[x]=father;
for(auto v:g[x])
if(v!=father)
dfs(v,x);
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin>>cas;
while(cas--)
{
cin>>n>>m;
a=1,b=0;
for(int i=1;i<=n;++i)
g[i].clear();
for(int i=1;i<n;++i)
{
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
if(m==1)
{
cout<<"1"<<endl;
continue;
}
dfs(1,0);
for(int i=fa[m];i!=0;i=fa[i])
{
int sz=g[i].size();
//在i点未走歪
a=a*inv(sz)%mod;//在i之前尚未走歪过
b=b*inv(sz)%mod;//在i之前已经走歪过一次
//在i点走歪了
b=b+a*(sz-1-(i!=1))%mod*inv(sz-1)%mod;//曾经走歪的概率+曾经未走歪的概率*i点走歪的概率=总的走歪概率
}
cout<<(a+b)%mod<<endl;
}
}
每个case都是O(n^2)暴力枚举轮数,然后判断合法就和已知的ans取min。
#include "bits/stdc++.h"
using namespace std;
int cas,a[4],ans;
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cin>>cas;
while(cas--)
{
ans=3000;
cin>>a[1]>>a[2]>>a[3];
sort(a+1,a+3+1);
for(int i=0;i<=1000;++i)
for(int j=0;j<=1000;++j)
{
int tmp=i+j;
int hp1=1000-i*a[2]-j*a[3];
int hp2=1000-i*a[1];
int hp3=1000-j*a[1];
if(hp2>0&&hp3>0)
{
//int rnd=min(hp2/a[3]+((hp2%a[3])?1:0),hp3/a[2]+((hp3%a[2])?1:0));
//上面是自己写的,下面是别人代码学来的,更方便的除法向上取整。
int rnd=min((hp2+a[3]-1)/a[3],(hp3+a[2]-1)/a[2]);
hp2-=a[3]*rnd;
hp3-=a[2]*rnd;
tmp+=rnd;
}
if((hp1<1&&hp2<1)||(hp1<1&&hp3<1)||(hp2<1&&hp3<1))
ans=min(ans,tmp);
}
cout<<ans<<endl;
}
}
标答给的启发式合并NTT,没学过暂不补。