2020.2.15模拟赛
剑与魔法
【问题描述】
万老师听说某大国很流行穿越,于是他就想写一个关于穿越的剧本。
闲话休提。话说老师穿越到了某一个剑与魔法的大陆。因为如此这般,所以老师从维娜艾那里得到了预言。老师一共被告知了若干件按顺序结算的 事件。这些事件分为两类:战役事件(CASE)、穿越回去事件(END)。战役事件可以选择是否参加,参加了之后会获得一定的金钱。每个END事件 发生需要至少参加一定数量的战役事件。特别的是,END事件如果满足要求就会强制发生。老师希望在大陆玩个够,所以他要求只有最后一个END事 件会发生。老师希望获得最多的金钱,所以求助于你。
【输入】
第一行一个数N,表示输入文件有多少行。 接下来每一行用空格隔开一个字符和一个整数。字符为“c”表示战役事件,接下来的整数表示这次涨RP顺带有多少钱;字符为“e”表示穿越回去 事件,接下来的整数代表至少要涨多少RP。最后一个事件保证是END事件。
【输出】
第一行一个整数,最多金钱数目。
若不可能则输出-1。
【输入输出样例】
dragons.in
5
c 10
c 12
e 2
c 1
e 2
dragons.out
13
【数据说明】
30%的数据满足 N<=20
60%的数据满足 N<=1,000
100%的数据满足 N<=200,000
每次涨RP事件赏金不超过10,000
穿越事件的要求不超过200,000
用堆(优先队列)实现贪心策略。尽量从堆中取出金钱。
#include<bits/stdc++.h> using namespace std; priority_queue<int> q; int main() { int n,h=0; scanf("%d",&n); for (int i=1;i<=n;i++) { char c; cin>>c; int x; scanf("%d",&x); if (c=='c') { x=-x; q.push(x); h++; } else { if (i!=n) { for (int i=1;i<=h-x+1;i++) q.pop(); if (h>=x) h=x-1; } } } int ans=0; for (int i=1;i<=h;i++) { int x; x=q.top(); q.pop(); ans-=x; } printf("%d",ans); }
矩形
【问题描述】
因为对polo忍无可忍, dzf使用圣剑在地上划出了许多纵横交错的沟壑来泄愤。这些沟壑都严格与X轴平行或垂直。 polo嘲笑了dzf无聊的行为,然后做了一件更加无聊的事。他蹲下来数这些沟壑的条数。数着数着,polo意识到一个问题,那就是因为圣剑的威力太大,划出的沟壑太多,地面就会塌陷。而如果两条水平的沟壑和两条垂直的沟壑相交组成了一个矩形,那么塌陷的危险就会进一步增加。现在polo已经数了n条沟壑,他想知道这些沟壑组成了多少个矩形。
【输入】
第一行一个数n,接下来每行4个数x1,y1,x2,y2,表示沟壑的两个端点(x1,y1),(x2,y2)
【输出】
一个数,组成的矩形个数。
【输入输出样例1】
rect.in
4
0 0 1 0
0 0 0 1
1 1 1 -1
1 1 0 1
rect.out
1
【输入输出样例2】
rect.in
8
1 0 4 0
2 1 2 0
0 0 0 3
2 2 2 3
3 3 3 -1
0 3 4 3
4 1 -1 1
3 2 -1 2
rect.out
6
【数据说明】
对于30%的数据,1<=n<=100
对于60%的数据,1<=n<=600
对于100%的数据,1<=n<=2000,坐标绝对值小于10^9,任意两条与X轴水平的沟壑之间没有交点,任意两条与X轴垂直的沟壑没有交点。
一开始打了个暴力O(n3)的算法,果然只拿了55分。
枚举两条垂直的线段,枚举一条水平线段,再枚举一条水平的与两者相交的线段k,明显能构成C(k,2)=k(k-1)/2个矩形。
但是O(n3)好像也能过,就优化了一点点吧,减少一点情况。
放两个代码对比。
55分代码。
#include<bits/stdc++.h> const int N=200005; using namespace std; int n,nx,ny; struct jsy { int x1,y1; int x2,y2; }x[N],y[N]; int main() { cin>>n; for(int i=1;i<=n;i++) { int t1,t2,t3,t4; cin>>t1>>t2>>t3>>t4; if(t2==t4) { x[++nx].x1=min(t1,t3); x[nx].y1=t2; x[nx].x2=max(t1,t3); x[nx].y2=t4; } if(t1==t3) { y[++ny].x1=t1; y[ny].y1=min(t2,t4); y[ny].x2=t3; y[ny].y2=max(t2,t4); } } if(n<4) { printf("0"); return 0; } long long ans=0; for(int i=1;i<=ny;i++) { for(int j=i+1;j<=ny;j++) { int t=0; for(int k=1;k<=nx;k++) { if(x[k].x1<=y[i].x1&&x[k].x1<=y[j].x1&&x[k].x2>=y[i].x1&&x[k].x2>=y[j].x1&&x[k].y1>=y[i].y1&&x[k].y1>=y[j].y1&&x[k].y1<=y[i].y2&&x[k].y1<=y[j].y2) t++; } ans+=t*(t-1)/2; } } cout<<ans; }
100分代码。
#include<bits/stdc++.h> using namespace std; const int N=2001; struct ahj { int a,b,c,d; }s[N],t[N]; int n,t1,t2; int q[N][N],w[N][N],c[N]; int main() { cin>>n; for(int i=1;i<=n;i++) { int a,b,c,d; cin>>a>>b>>c>>d; if(b==d) { if(a>c) swap(a,c); s[++t1]=ahj{a,b,c,d}; } else { if(b>d) swap(b,d); t[++t2]=ahj{a,b,c,d}; } } for(int i=1;i<=t1;i++) { for(int j=1;j<=t2;j++) { if(s[i].a<=t[j].a&&t[j].a<=s[i].c&&t[j].b<=s[i].b&&s[i].b<=t[j].d) { q[i][j]=1; w[i][++c[i]]=j; } } } int ans=0; for(int i=1;i<t1;i++) { for(int j=i+1;j<=t1;j++) { int k=0; int x=i,y=j; if(c[i]>c[j]) swap(x,y); for(int h=1;h<=c[x];h++) { if(q[y][w[x][h]]) k++; } ans+=k*(k-1)/2; } } printf("%d",ans); return 0; }
但是正解貌似是线段树。
线段树+离散化,利用线段树的区间求和来解决,总时间复杂度为O(n2log2n)。
但是我没有写,没有代码就算了吧。
数列的GCD
【问题描述】
给出一个长度为N的数列{a[n]},1<=a[i]<=M(1<=i<=N)。
现在问题是,对于1到M的每个整数d,有多少个不同的数列b[1], b[2], ..., b[N],满足:
(1)1<=b[i]<=M(1<=i<=N);
(2)gcd(b[1], b[2], ..., b[N])=d;
(3)恰好有K个位置i使得a[i]<>bi
注:gcd(x1,x2,...,xn)为x1, x2, ..., xn的最大公约数。
输出答案对1,000,000,007取模的值。
【输入】
第一行包含3个整数,N,M,K。
第二行包含N个整数:a[1], a[2], ..., a[N]。
【输出】
输出M个整数到一行,第i个整数为当d=i时满足条件的不同数列{b[n]}的数目mod 1,000,000,007的值。
【输入输出样例1】
gcd.in
3 3 3
3 3 3
gcd.out
7 1 0
【输入输出样例2】
gcd.in
3 5 3
1 2 3
gcd.out
59 3 0 1 1
【样例1解释】
当d=1,{b[n]}可以为:(1, 1, 1), (1, 1, 2), (1, 2, 1), (1, 2, 2), (2, 1, 1), (2, 1, 2), (2, 2, 1)。
当d=2,{b[n]}可以为:(2, 2, 2)。
当d=3,因为{b[n]}必须要有k个数与{a[n]}不同,所以{b[n]}不能为(3, 3, 3),满足条件的一个都没有。
【数据说明】
对于30%的数据,1<=N<=20, 1<=M<=2。
对于50%的数据,1<=N,M<=1000。
对于70%的数据,1<=N,M<=10000。
对于100%的数据,1<=N,M<=300000, 1<=K<=N, 1<=a[i]<=M。
可以推出下列公式:
ans [ d ] = C(s,sum) * ([m/d]-1)^(sum-s) * ([m/d])^(n-sum)
d表示公倍数,s是与原数列要相同的数的个数(即n-k),sum为在a数列中是d倍数的数的个数。
注意取模,还有阶乘的处理。
#include<bits/stdc++.h> using namespace std; #define ll long long const int N=5e5+7,M=1e9+7; int n,m,k; ll f[N],ans[N]; int cnt[N]; ll st(ll x,ll y) { ll res=1; while(y) { if(y&1) res=(res*x)%M; x=x*x%M; y>>=1; } return res; } ll C(ll n,ll m) { return f[m]*st(f[n],M-2)%M*st(f[m-n],M-2)%M; } int main() { int a; cin>>n>>m>>k; for(int i=1;i<=n;i++) { scanf("%d",&a); cnt[a]++; } f[0]=1; for(int i=1;i<=n;i++) f[i]=f[i-1]*i%M; int s=n-k; for(int i=m;i;i--) { int sum=0; for(int j=i;j<=m;j+=i) sum+=cnt[j]; if(sum<s) { ans[i]=0; continue; } ans[i]=C(s,sum)*st(m/i-1,sum-s)%M*st(m/i,n-sum)%M; for(int j=i*2;j<=m;j+=i) ans[i]=(ans[i]+M-ans[j])%M; } for(int i=1;i<m;i++) printf("%lld ",ans[i]); printf("%lld",ans[m]); return 0; }