2021“MINIEYE杯”中国大学生算法设计超级联赛7
1003 Fall with Trees
答案为一个三角的面积加上一堆梯形的面积(其实三角形也可以理解为上底为0的梯形doge),它们的高一定。
答案为设第x个梯形(包括三角)的下底为f(x)显然有
$f[1]=0,f[2]=x_{ron}-x_{lson},f[x+1]=f[x]*(2^x-2) $
然后答案就是 (sum_{i=2}^k y*(f[i]-f[i-1])),发现可以把y和(x_{ron}-x_{lson})提出来
变成(y*(x_{ron}-x_{lson})sum_{i=2}^k (g[i]-g[i-1]))其中(g[i]=frac{f[i]}{x_{ron}-x_{lson}})
g[i]显然可以递推。所以这题就解决了?
其实这个题精度要求很低,g[i]最后会变成一个恒定的数。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define int long long
#define N 10100
double pw[N],a[N],sum[N];
void pre_work(){
pw[0]=1;
for(int i=1;i<=1000;i++)pw[i]=pw[i-1]*2;
a[2]=1;
for(int i=3;i<=10000;i++){
if(i>1000)a[i]=a[i-1];
else a[i]=a[i-1]+a[i-1]/(pw[i-1]-2);
}
for(int i=2;i<=10000;i++){
sum[i]=sum[i-1]+a[i]+a[i-1];
}
}
signed main(){
int T;
scanf("%lld",&T);
pre_work();
while(T--){
int k;
scanf("%lld",&k);
int x_1,y_1,x_2,y_2,x_3,y_3;
scanf("%lld%lld%lld%lld%lld%lld",&x_1,&y_1,&x_2,&y_2,&x_3,&y_3);
printf("%.3lf
",(y_1-y_2)*(x_3-x_2)*sum[k]/2);
}
return 0;
}
1004 Link with Balls
表示题意没看懂。
就是给出2n个篮子其中n个可以取i的倍数个,其他n个可以取0~i个。
然后让你求拿m个方案数。
如图是n=3的情况。
可以将可以取i的倍数个的和可以取0~i个的分成一组,这一组就可以拿随意个且方法唯一。
(第一个可以取1的倍数的单独一组,最后可以取0~n个的单独一组)。
结果是有n组可以取随意个,1组可以取0~n个。
然后枚举最后一组拿的个数i。另外n组的方案就是m-i个物品用n-1个板子隔开(不能有空)的方案。
答案为(sum_{i=0}^{n}C_{m-i+n-1}^{n-1}=C_{m+n}^{n}-C_{m-1}^{n})
这个转化?杨辉三角找规律?
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
#define int long long
#define N 2010000
int fac[N],inv[N];
const int p=1e9+7;
int ksm(int x,int b){
int ans=1;
while(b){
if(b&1)ans=ans*x%p;
b>>=1;
x=x*x%p;
}
return ans;
}
void pre_work(){
fac[0]=1;
for(int i=1;i<=2000000;i++)fac[i]=fac[i-1]*i%p;
for(int i=1;i<=2000000;i++)inv[i]=ksm(fac[i],p-2);
}
int C(int n,int m){
if(n<m)return 0;
return fac[n]*inv[m]%p*inv[n-m]%p;
}
signed main(){
int T;
scanf("%lld",&T);
pre_work();
while(T--){
int n,m;
scanf("%lld%lld",&n,&m);
printf("%lld
",(C(n+m,n)-C(m-1,n)+p)%p);
}
return 0;
}
1005 Link with EQ
题面太真实了。
注意到只要不选一开始不选第x个座位(xin{1,2,n-1,n-2})第1和第n个座位早晚会坐上人。而且一定是x两侧最先选的(像极了自己坐角落),然后剩下的问题其实就是在最左空位的左侧和最右空位的右侧都有人了的连续空区间做人的问题。
然后我打出了表并AC了本题
设长度为x的这样的区间期望坐下的人数为f(x),显然有(f(x)=f(lfloorfrac{n-1}{2} floor)+f(n-lfloorfrac{n-1}{2} floor-1)+1),(f(1)=f(2)=0)
对于(xin{1,2,n-1,n-2})情况答案为(f(n-2)+2,f(n-3)+2,f(n-3)+2,f(n-2)+2)
否则答案为(f(x-2)+f(n-x)+3)。最后统计一下答案就是(2*sum[n-2]+8+(n-4)*3),其中sum是f的前缀和
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define int long long
#define N 5110000
const int p=1e9+7;
int ans[N],inv[N],pw[N],sum[N];
int ksm(int x,int b){
int ans=1;
while(b){
if(b&1)ans=ans*x%p;
b>>=1;
x=x*x%p;
}
return ans;
}
void pre_work(){
for(int i=1;i<=1000000;i++){
int x=ksm(i,p-2);
inv[i]=x;
}
pw[0]=1;
for(int i=1;i<=22;i++)pw[i]=pw[i-1]*2;
for(int i=0;i<=20;i++){
int begin=pw[i]*2-1;
int w=pw[i]-1;
int end=pw[i+1]*2-2;
for(int j=begin;j<=end;j++)
ans[j]=w;
if(i==0)continue;
for(int j=w-1;j>pw[i-1]-1;j--)ans[begin-w+j]=j;
}
for(int i=1;i<=1000000;i++)sum[i]=(sum[i-1]+ans[i])%p;
}
signed main(){
pre_work();
int T;
scanf("%lld",&T);
while(T--){
int n;
scanf("%lld",&n);
if(n==1)printf("1
");
else if(n==2)printf("1
");
else if(n==3)printf("666666673
");
else if(n==4)printf("2
");
else printf("%lld
",((2+ans[n-2]+2+ans[n-3]+sum[n-4])*2+(n-4)*3)%p*inv[n]%p);
}
return 0;
}
1006 Link with Grenade
一个比较妙的转化:给人和炸弹一个向上的g。一个显然的事是扔炸弹只跟仰角有关,所以问题可以转到平面上。
人就是方向向上的匀加速直线运动,炸弹是匀速直线运动。t时间之后设人和炸弹到达A点和B点,而且轨迹都是以原点为起点的线段并且线段长度已知。
考虑被炸死的极限情况人和炸弹的距离正好为r,这种情况下A,B,O构成了一个三角形。用余弦定理可以求得OA、OB的夹角(phi),这其实就是最大的跟竖直方向的夹角。然后考虑怎么根据这个(cosphi)算出概率。
根据球冠表面积公式可以炸到人的角度对应球冠的表面积为(2pi(v_0t)^2(1-cosphi)),除以总表面积(4pi(v_0t)^2)就是概率。
然后处理构成不了三角形的情况,这种情况就是必定被炸死和必定炸不死的情况。
(ps:一个概率为0的事件也可能发生)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define int long long
const int p=1e9+7;
int ksm(int x,int b){
int ans=1;
while(b){
if(b&1)ans=ans*x%p;
b>>=1;
x=x*x%p;
}
return ans;
}
signed main(){
int T,t,v,r;
scanf("%lld",&T);
while(T--){
scanf("%lld%lld%lld",&t,&v,&r);
int A=5*t*t;
int B=v*t;
int C=r;
if(B-A>=C||A-B>=C)printf("1
");
else if(B+A<=C)printf("0
");
else {
printf("%lld
",(1-(1-(A*A%p+B*B%p-C*C%p+p)%p*ksm(2*A*B%p,p-2)%p+p)%p*ksm(2,p-2)%p+p)%p);
}
}
return 0;
}
1007 Link with Limit
把每个点向下一个到达的点连边。模拟转移过程发现一个点最后总会在一个圈里转圈。
根据对极限的理解g(x)其实跟最后到达的那个环的平均值有关。
判断所有环的平均值时候相等即可。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
#define N 101000
#define int long long
int c[N],fa[N],a[N],in[N],sum[N],tot[N];
double w[N];
void dfs(int u,int x){
if(c[u]!=0)return;
c[u]=x;
dfs(fa[u],x);
}
signed main(){
int T;
scanf("%lld",&T);
while(T--){
int n;
scanf("%lld",&n);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
for(int i=1;i<=n;i++)in[i]=0;
for(int i=1;i<=n;i++)fa[i]=a[i],in[a[i]]++;
queue<int> q;
for(int i=1;i<=n;i++)if(in[i]==0)q.push(i);
while(!q.empty()){
int u=q.front();
q.pop();
in[fa[u]]--;
if(in[fa[u]]==0)q.push(fa[u]);
}
int num=0;
for(int i=1;i<=n;i++)c[i]=0;
for(int i=1;i<=n;i++)
if(c[i]==0&&in[i])dfs(i,++num);
for(int i=1;i<=num;i++)sum[i]=tot[i]=0;
for(int i=1;i<=n;i++)
if(c[i])sum[c[i]]+=a[i],tot[c[i]]++;
for(int i=1;i<=num;i++)w[i]=(double)sum[i]/tot[i];
bool flag=0;
for(int i=2;i<=num;i++)
if(w[i]!=w[1]){
printf("NO
");
flag=1;
break;
}
if(flag==1)continue;
printf("YES
");
}
return 0;
}
1010 Smzzl with Tropical Taste
签到
1012 Yiwen with Sqc
对于每个字符单独考虑。考虑x时,求出x出现的位置a[i],特别地a[0]=0,a[cnt+1]=n+1。
然后处理出以第i个字母为最开始出现字母区间左端点的可能选择数量l[i]和以第i个字母为最后出现字母区间右端点的可能选择数量r[i],发现r[i]=l[i+1],设b[i]=l[i],b[i]=a[i]-a[i-1]。
然后答案就是(sum_{len=1}^{cnt}len^2sum_{i=1}^{cnt-len}b[i]*b[i+len])
好然后上卷积,T了
考虑O(n)处理这个东西。
对于b[i],考虑它跟(b[j](j<i))构成的贡献=(b[i]*sum_{j=1}^{i-1}b[j]*(i-j)^2),总答案就是(sum_{i=2}^{n}b[i]*sum_{j=1}^{i-1}b[j]*(i-j)^2)
考虑维护(sum_{j=1}^{i-1}b[j]*(i-j)^2),发现b[j]的系数为1,4,9,16,25.....(n^2),变化的值就是(b[i]+sum_{j=1}^{i-1}b[j]*[1+2*(i-j)]),这个变化的值变化的值(doge)就是(b[i]+sum_{j=1}^{i-1}b[j]*2)。
求出这个变化值的变化值,进而求出变化值,进而求出答案。
#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define int long long
const int p=998244353;
const int N=501000;
int a[N];
int n,m;
char s[N];
int sum[N],num[N],cnt[N];
int D_a[N],S[N];
vector<int> w[50];
signed main(){
int T;
scanf("%lld",&T);
while(T--){
scanf("%s",s + 1);
n = strlen(s + 1);
for(int i = 1;i <= 26;++i) w[i].clear(),w[i].push_back(0),cnt[i]=1;
for(int i = 1;i <= n;++i){
int x = s[i] - 'a' + 1;
w[x].push_back(i);
cnt[x]++;
}
for(int i=1;i<=26;i++)w[i].push_back(n+1);
int ans=0;
for(int k=1;k<=26;k++){
n=m=cnt[k]-1;
if(n<=0)continue;
for(int i=0;i<=n;i++)a[i]=w[k][i+1]-w[k][i];
for(int i=0;i<=n;i++)
if(i==0)D_a[i]=2*a[i]%p;
else D_a[i]=(D_a[i-1]+a[i]*2)%p;
for(int i=0;i<=n;i++){
if(i==0)S[i]=a[i];
else S[i]=(S[i-1]+D_a[i-1]+a[i])%p;
}
int tmp=0;
for(int i=0;i<=n;i++){
ans=(ans+tmp*a[i]%p)%p;
tmp=(tmp+S[i])%p;
}
}
printf("%lld
",ans);
}
return 0;
}