第 i i i 种套餐在第一次吃饭时省了 ( 1 − c i ) × a (1-c_i)\times a (1−ci)×a 元,本来应该付的钱就是 b i × a + ( 1 − c ) × a b_i\times a +(1-c)\times a bi×a+(1−c)×a,除一下得到优惠比例 1 − c i b i + ( 1 − c i ) \dfrac {1-c_i} {b_i+(1-c_i)} bi+(1−ci)1−ci。
话说这题我居然看了三分钟才看懂题意啊……(最后还是靠一手样例才get到它的意思qwq)
代码如下:
#include
int T,n;
int main()
{
scanf("%d",&T);while(T--)
{
scanf("%d",&n);
int x;double y,ans=0;
for(int i=1;i<=n;i++){
scanf("%d %lf",&x,&y);y=1.0-y;
if(y/(1.0*x+y)>ans)ans=y/(1.0*x+y);
}
printf("%.5lf\n",ans);
}
}
假如 p > 1.0 p>1.0 p>1.0,那么肯定拿到了多的那一堆,肯定不交换。
假如 p ≤ 1.0 p\leq 1.0 p≤1.0,简单计算一下,可以知道交换后得到的金币数量期望为 1 2 × p 2 + 1 2 × 2 p = 5 4 p > p \dfrac 1 2\times \dfrac p 2+\dfrac 1 2\times 2p=\dfrac 5 4p>p 21×2p+21×2p=45p>p,所以要换。
代码如下:
#include
int T;double n;
int main()
{
scanf("%d",&T);while(T--)
{
scanf("%lf",&n);
if(n<=1.0)printf("Yes\n");
else printf("No\n");
}
}
容易发现,每次肯定交换头尾的两个数字,先换 1 , n 1,n 1,n,再换 2 , n − 1 2,n-1 2,n−1,以此类推,这样的逆序对数一定是最大的,然后推一推每次交换的贡献即可。
代码如下:
#include
int T;
long long n,m;
int main()
{
scanf("%d",&T);while(T--)
{
scanf("%lld %lld",&n,&m);
if(m>=n/2)printf("%lld\n",n*(n-1)/2ll);
else printf("%lld\n",m+2ll*n*m-2*m*(m+1));
}
}
考虑拐角处的四个位置,设左上角坐标为 ( 1 , 1 ) (1,1) (1,1)。
每个时刻,在 ( 1 , 2 ) (1,2) (1,2) 和 ( 2 , 2 ) (2,2) (2,2) 的车肯定右移一步直接到达线 y y y 东边, ( 1 , 1 ) (1,1) (1,1) 位置上的车也肯定右移一步因为别无选择,需要考虑的是 ( 2 , 1 ) (2,1) (2,1) 上的车。
假如在拐角的下面还有车,那么让 ( 2 , 1 ) (2,1) (2,1) 上的车挪到 ( 1 , 1 ) (1,1) (1,1) 位置肯定不亏,因为如果挪到 ( 2 , 2 ) (2,2) (2,2),有可能挡住下面的车。
然后再判断一下有没有车能填到 ( 1 , 2 ) (1,2) (1,2) 和 ( 2 , 2 ) (2,2) (2,2) 位置即可,代码如下:
#include
#include
#include
#include
using namespace std;
int T,n,a[3][3];
vector<int>l,r;
bool check(){
for(int i=1;i<=2;i++)
for(int j=1;j<=2;j++)if(a[i][j])return true;
return l.size()>0||r.size()>0;
}
bool cmp(int x,int y){return x>y;}
int main()
{
scanf("%d",&T);while(T--)
{
l.clear();r.clear();
scanf("%d",&n);
for(int i=1,id,x;i<=n;i++){
scanf("%d %d",&id,&x);
if(id==1)r.push_back(x);
else l.push_back(x);
}
sort(l.begin(),l.end(),cmp);
sort(r.begin(),r.end(),cmp);
int ans=0;while(check()){
ans++;
a[1][2]=a[2][2]=0;
a[1][2]=a[1][1];a[1][1]=0;
if(l.size()||r.size())a[1][1]=a[2][1];
else a[2][2]=a[2][1];
a[2][1]=0;
if(l.size()&&l.back()-ans<=0)a[2][1]=1,l.pop_back();
if(r.size()&&r.back()-ans<=0)a[2][2]=1,r.pop_back();
}
printf("%d\n",ans);
}
}
容易发现,不管传送器设置传送到哪里,我们都只关心:是否存在连续 11 11 11 个传送器。
如果存在,那么肯定不能跨过,如果不存在,那么一定可以到达终点,于是dp一下即可。
代码如下:
#include
#include
#include
using namespace std;
#define maxn 1010
#define mod 1000000007
int T,n,m;
int f[maxn][maxn];//f[i][j]表示前i位放j个传送器,第i位不放传送器,且不存在11个连续传送器的方案数
void add(int &x,int y){x=(x+y>=mod?x+y-mod:x+y);}
void dp(){
f[1][0]=1;
for(int i=2;i<=maxn-10;i++){
f[i][0]=1;
for(int j=1;j<=maxn-10;j++){
if(j>i-2){f[i][j]=-1;continue;}
//k枚举的是上一个没有传送器的位置,且k+1~i-1全是传送器
//fac表示k+1~i-1中间的传送器有多少种设置传送地点的方案
for(int k=i-1,fac=1;i-k-1<11&&i-k-1<=j&&k>=1;fac=1ll*fac*(k-1)%mod,k--)
if(f[k][j-(i-k-1)]!=-1)add(f[i][j],1ll*f[k][j-(i-k-1)]*fac%mod);
if(!f[i][j])f[i][j]=-1;
}
}
}
int main()
{
scanf("%d",&T);dp();
while(T--){
scanf("%d %d",&n,&m);
printf("%d\n",f[n][m]);
}
}
如果要让第二只蚂蚁概率大于等于 1 2 \dfrac 1 2 21,那么在第一只蚂蚁走过的路上, 1 1 1 到石榴的路径上分叉不超过 1 1 1 处,枚举一下分叉点计算一下概率就好了。
代码如下:
#include
#include
#include
using namespace std;
#define maxn 100010
#define mod 1000000007
int T,n,m,du[maxn];
struct edge{int y,next;}e[maxn<<1];
int first[maxn],len;
void buildroad(int x,int y){e[++len]=(edge){y,first[x]};first[x]=len;}
int ksm(int x,int y){int re=1;for(;(y&1?re=1ll*re*x%mod:0),y;y>>=1,x=1ll*x*x%mod);return re;}
int fa[maxn];void dfs(int x){
for(int i=first[x],y;i;i=e[i].next)
if((y=e[i].y)!=fa[x])fa[y]=x,dfs(y);
}
int add(int x){return x>=mod?x-mod:x;}
int main()
{
scanf("%d",&T);while(T--)
{
scanf("%d %d",&n,&m);
memset(first,0,(n+1)<<2);len=0;
memset(du,0,(n+1)<<2);
for(int i=1,x,y;i<n;i++)scanf("%d %d",&x,&y),
buildroad(x,y),buildroad(y,x),du[x]++,du[y]++;
if(m==1){printf("1\n");continue;}
dfs(1);
int dir=1,ans=0;
for(int i=fa[m];i;i=fa[i])dir=1ll*dir*ksm(du[i],mod-2)%mod;
ans=dir;
for(int i=fa[m];i!=1;i=fa[i])
if(du[i]>2)ans=add(ans+1ll*dir*(du[i]-2)%mod*ksm(du[i]-1,mod-2)%mod);
if(du[1]>1)ans=add(ans+dir);
printf("%d\n",ans);
}
}
设攻击力最大的人为 a a a,次大的为 b b b,另一个是 c c c。
考虑贪心,要让次数最少,肯定先让 a a a 和 b b b 打, b b b 死了之后再让 a a a 和 c c c 打。
有一种特殊情况,假如 a a a 比 b , c b,c b,c 的攻击力加起来都大,那么 a a a 是可以一个人k死另外两个人的,此时 a a a 可能会对 b , c b,c b,c 造成过量伤害,这其实是不优的,考虑让 a a a 少和 b , c b,c b,c 打几轮,取而代之的,让 b , c b,c b,c 互博几轮,这可能是更优的,枚举一下 b , c b,c b,c 互博的轮数即可。
举个例子,三个人攻击力分别为 3 , 1 , 1 3,1,1 3,1,1,当 a a a 将 b , c b,c b,c 打剩 1 1 1 滴血时,让他们互博一次,那么他们就都死了,不需要 a a a 分别k死他们两个。
时间复杂度为 O ( t ) O(t) O(t) ~ O ( t n ) O(tn) O(tn),但是我比较懒……互博的过程是可以 O ( 1 ) O(1) O(1) 实现的,我写了个 O ( n ) O(n) O(n) 的,但是还是可以无压力 0 s 0s 0s 跑过。
代码如下:
#include
#include
using namespace std;
int T,tot;
struct par{int x,y;}a[10];
bool cmp(par x,par y){return x.y>y.y;}
void fight(int x,int y){a[x].x-=a[y].y;a[y].x-=a[x].y;tot++;}
int main()
{
scanf("%d",&T);while(T--)
{
scanf("%d %d %d",&a[1].y,&a[2].y,&a[3].y);
a[1].x=a[2].x=a[3].x=1000;tot=0;int ans=999999999;
sort(a+1,a+4,cmp);
if(a[1].y>=a[2].y+a[3].y){
for(int i=0;i<=1000;i++){
a[1].x=a[2].x=a[3].x=1000;tot=i;
a[2].x-=a[3].y*i;a[3].x-=a[2].y*i;
if(a[i].x+a[3].y<=0||a[3].x+a[2].y<=0)break;
while(a[1].x>0&&a[2].x>0)fight(1,2);
while(a[1].x>0&&a[3].x>0)fight(1,3);
ans=min(ans,tot);
}
}else{
while(a[1].x>0&&a[2].x>0)fight(1,2);
while(a[1].x>0&&a[3].x>0)fight(1,3);
ans=tot;
}
printf("%d\n",ans);
}
}
赛时并没有做出来,因为太菜了。
限制相当于,每个环内都有奇数个奇数。观察可以发现,图一定是仙人掌。
证明用反证法,考虑两个环的公共部分,设它包含 a a a 个奇数,两个环的不公共部分分别含有 b , c b,c b,c 个奇数,那么有 a + b ≡ 1 ( m o d 2 ) , a + c ≡ 1 ( m o d 2 ) a+b\equiv 1\pmod 2,a+c\equiv 1\pmod 2 a+b≡1(mod2),a+c≡1(mod2),则有 2 a + b + c ≡ 0 ( m o d 2 ) 2a+b+c\equiv 0\pmod 2 2a+b+c≡0(mod2),模 2 2 2 后 2 a 2a 2a 会被模掉,相当于 b + c ≡ 0 ( m o d 2 ) b+c\equiv 0 \pmod 2 b+c≡0(mod2),即存在一个不满足要求的简单环,所以任意两个环不可能有公共边。
然后可以对每个环构造一个多项式, x i x^i xi 的系数表示放 i i i 个奇数的方案数。设环的大小为 S S S, i i i 为奇数时, x i x^i xi 的系数为 C S i C_S^i CSi,表示选出 i i i 个位置放奇数,否则 x i x^i xi 的系数为 0 0 0。
对于非环内边则可放奇数可不放奇数,构造的多项式为 1 + x 1+x 1+x,然后将所有多项式乘起来,取 x ⌈ n 2 ⌉ x^{\lceil \frac n 2 \rceil} x⌈2n⌉ 的系数就是答案。
最后还要乘以 ⌈ n 2 ⌉ ! × ⌊ n 2 ⌋ ! \lceil \frac n 2 \rceil !\times \lfloor \frac n 2 \rfloor! ⌈2n⌉!×⌊2n⌋!,因为每个奇数和每个偶数都是不同的,上面只考虑了给他们安排位置。
代码如下:
#include
#include
#include
#include
#include
using namespace std;
#define maxn 300010
#define mod 998244353
#define bin(x) (1<<(x))
int T,n,m;
struct edge{int y,next;}e[maxn];
int first[maxn],len=0;
void buildroad(int x,int y){e[++len]=(edge){y,first[x]};first[x]=len;}
int deep[maxn],fa[maxn];
void dfs(int x){
for(int i=first[x];i;i=e[i].next)
if(e[i].y!=fa[x])deep[e[i].y]=deep[x]+1,fa[e[i].y]=x,dfs(e[i].y);
}
bool vis[maxn];
int go(int x,int y)
{
if(deep[x]>deep[y])swap(x,y); int re=1;
while(deep[y]>deep[x]){
if(vis[y])return -1;
vis[y]=true; y=fa[y],re++;
}
while(x!=y){
if(vis[x]||vis[y])return -1;
vis[x]=vis[y]=true;
x=fa[x];y=fa[y];re+=2;
}
return re;
}
struct BCJ{
int Fa[maxn];void init(int N){for(int i=1;i<=N;i++)Fa[i]=i;}
int findfa(int x){return x==Fa[x]?x:Fa[x]=findfa(Fa[x]);}
bool link(int x,int y){
x=findfa(x),y=findfa(y);
if(x==y)return false;
Fa[y]=x;return true;
}
}K;
struct par{int x,y;};
vector<par>h;
int add(int x){return x>=mod?x-mod:x;}
int dec(int x){return x<0?x+mod:x;}
int inv[maxn];
int ksm(int x,int y){int re=1;for(;(y&1?re=1ll*re*x%mod:0),y;y>>=1,x=1ll*x*x%mod);return re;}
struct NTT{
int w[maxn];NTT(){int N=bin(18);
inv[1]=1;for(int i=2;i<=N;i++)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
for(int i=1,wn;i<N;i<<=1){
w[i]=1;wn=ksm(3,(mod-1)/(i<<1));
for(int j=1;j<i;j++)w[i+j]=1ll*w[i+j-1]*wn%mod;
}
}
int limit,r[maxn];
void work(int lg){r[0]=0;for(int i=1;i<bin(lg);i++)r[i]=(r[i>>1]>>1)|((i&1)<<(lg-1));}
void dft(int *f,int lg,int type=0)
{
limit=bin(lg);if(type)reverse(f+1,f+limit);
for(int i=1;i<limit;i++){
if(i<r[i])swap(f[i],f[r[i]]);
}
for(int mid=1;mid<limit;mid<<=1)for(int j=0;j<limit;j+=(mid<<1))for(int i=0;i<mid;i++)
{int t=1ll*f[j+i+mid]*w[i+mid]%mod;f[j+i+mid]=dec(f[j+i]-t);f[j+i]=add(f[j+i]+t);}
}
}ntt;
int A[maxn],B[maxn];
struct POLY{
vector<int> a;int len;void rs(int N){a.resize(len=N);}POLY(){len=1;a.clear();}
int &operator [](int x){return a[x];}
void dft(int *A_,int lg,int ln){for(int i=0;i<bin(lg);i++)A_[i]=(i<min(ln,len)?a[i]:0);ntt.dft(A_,lg);}
void idft(int *A_,int lg,int ln){ntt.dft(A_,lg,1);rs(ln);for(int i=0;i<ln;i++)a[i]=1ll*A_[i]*inv[bin(lg)]%mod;}
POLY Mul(POLY b,int ln=0){
if(!ln)ln=len+b.len-1;int lg=ceil(log2(ln));
ntt.work(lg);dft(A,lg,ln);b.dft(B,lg,ln);
for(int i=0;i<bin(lg);i++)A[i]=1ll*A[i]*B[i]%mod;b.idft(A,lg,ln);return b;
}
}F[maxn];
int fac[maxn],inv_fac[maxn];
void work(){
fac[0]=inv_fac[0]=1;for(int i=1;i<=maxn-10;i++)fac[i]=1ll*fac[i-1]*i%mod;
inv_fac[maxn-10]=ksm(fac[maxn-10],mod-2);
for(int i=maxn-11;i>=1;i--)inv_fac[i]=1ll*inv_fac[i+1]*(i+1)%mod;
}
int C(int x,int y){return 1ll*fac[x]*inv_fac[y]%mod*inv_fac[x-y]%mod;}
void solve(int l,int r){
if(l==r)return;
int mid=(l+r)>>1;solve(l,mid);solve(mid+1,r);
F[l]=F[l].Mul(F[mid+1]);
}
int main()
{
scanf("%d",&T);work();while(T--)
{
scanf("%d %d",&n,&m);
h.clear();K.init(n);
memset(vis,false,sizeof(vis));
memset(first,0,sizeof(first));len=0;
for(int i=1,x,y;i<=m;i++){
scanf("%d %d",&x,&y);
if(!K.link(x,y))h.push_back((par){x,y});
else buildroad(x,y),buildroad(y,x);
}
dfs(1); int t,tot=0;
for(int i=0;i<h.size();i++){
t=go(h[i].x,h[i].y);
if(t==-1)break;
F[++tot].rs(t+1);
for(int j=0;j<=t;j++)F[tot][j]=(j&1?C(t,j):0);
}
for(int i=2;i<=n;i++)if(!vis[i]){
F[++tot].rs(2);
F[tot][0]=F[tot][1]=1;
}
if(t==-1){printf("0\n");continue;}
solve(1,tot);
printf("%d\n",1ll*F[1][(m+1)/2]*fac[(m+1)/2]%mod*fac[m/2]%mod);
}
return 0;
}