牛客网多校第五场
A:gpa
分数规划裸题了吧,然后二分次数过多的话会超时
#include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; const int MAX=1e5+5; int n,k; double s[MAX],c[MAX]; double tmp[MAX]; int check(double x){ int i; double ans=0; for(i=0;i<n;i++) tmp[i]=s[i]*c[i]-x*s[i]; sort(tmp,tmp+n); for(i=n-1;i>=k;i--) ans+=tmp[i]; if(ans>0) return 1; return 0; } double bsearch(double left,double right){ int i; double mid; for(i=0;i<60;i++){ mid=(left+right)/2; if(check(mid)) left=mid; else right=mid; } return mid; } int main(){ int i; scanf("%d%d",&n,&k); for(i=0;i<n;i++) scanf("%lf",&s[i]); for(i=0;i<n;i++) scanf("%lf",&c[i]); double ans=bsearch(0,1e9); printf("%.7lf\n",ans); return 0; }
B: div
主要是结论比较难构造出来,当然如果oeis技巧足够强,这就是小事。推出结论就很简单了,java大数随便写写就好了
附结论证明
import java.math.BigInteger; import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner cin = new Scanner(System.in); BigInteger m=cin.nextBigInteger(); BigInteger a1 =new BigInteger("0"); BigInteger a2 =new BigInteger("2"); BigInteger b1 =new BigInteger("0"); BigInteger b2 =new BigInteger("6"); BigInteger a3,b3; BigInteger k1 =new BigInteger("6"); BigInteger k2 =new BigInteger("14"); while(a2.compareTo(m)<=0) { a3=a2.multiply(k1).subtract(a1); a1=a2; a2=a3; } while(b2.compareTo(m)<=0) { b3=b2.multiply(k2).subtract(b1); b1=b2; b2=b3; } System.out.println(a2.min(b2)); } }
D: inv
b数组的逆序数对拿树状数组搞一搞。性质:得到最优解的插入顺序一定会按照升序插入,所以不用care插入的先后顺序,直接按当前的最优解来就可以,线段树维护答案
#include <cstdio> #include <cstring> #include <queue> #include <algorithm> #include <cmath> #include <vector> #include <iostream> #include <stack> #include <set> #include <map> #define ll __int128 #define LSON l,m,x<<1 #define RSON m+1,r,x<<1|1 using namespace std; const int MAX=2e5+5; int n,a[MAX],b[MAX],c[MAX],pos[MAX]; long long ans; int lowbit(int x){ return x&(-x); } void add(int pos,long long val){ //单点加值 while(pos<MAX){ a[pos]=a[pos]+val; pos+=lowbit(pos); } } long long getsum(int pos){ //1--pos求和 long long sum=0; while(pos>0){ sum=sum+a[pos]; pos-=lowbit(pos); } return sum; } int lazy[MAX*4],xds[MAX*4]; void pushup(int x){ xds[x]=min(xds[x<<1],xds[x<<1|1]); } void pushdown(int x){ if(lazy[x]){ xds[x<<1]+=lazy[x]; xds[x<<1|1]+=lazy[x]; lazy[x<<1]+=lazy[x]; lazy[x<<1|1]+=lazy[x]; lazy[x]=0; } } void js(int l,int r,int x){ int m=(l+r)/2; if(l==r){ xds[x]=c[l]; return; } js(LSON); js(RSON); lazy[x]=0; pushup(x); } void xg(int L,int R,int val,int l,int r,int x){ int m=(l+r)/2; if(l==L&&r==R){ xds[x]+=val; lazy[x]+=val; return; } pushdown(x); if(R<=m) xg(L,R,val,LSON); else if(L>m) xg(L,R,val,RSON); else{ xg(L,m,val,LSON); xg(m+1,R,val,RSON); } pushup(x); } int main(){ int i; scanf("%d",&n); int m=n/2; for(i=1;i<=m;i++){ scanf("%d",&b[i]); pos[b[i]]=i; add(b[i],1); ans+=i-getsum(b[i]); } m++; for(i=1;i<=m;i++) c[i]=i-1; js(1,m,1); for(i=3;i<n;i+=2){ xg(1,pos[i-1],1,1,m,1); xg(pos[i-1]+1,m,-1,1,m,1); ans+=xds[1]; } printf("%lld\n",ans); return 0; }
E:room
二分图最大权值匹配,将旧安排和新安排两两之间建边,容量为1,权值为这两个房间相同人的数量。因为凡是前后不在同一个宿舍的人可以只通过一次就交换到新宿舍,所以只要不交换的人尽量多即可
#include <cstdio> #include <cstring> #include <queue> #include <algorithm> #include <cmath> #include <vector> #include <stack> #include <set> using namespace std; const int INF=0x3f3f3f3f; const int MAX=1e3+5; const int ext=200; struct node{ int v,f,cost,next; }edge[MAX*MAX]; int no,s,t; int head[MAX],dis[MAX],vis[MAX],pre[MAX],rec[MAX]; //pre记录前驱 rec记录边在edge中编号 queue<int> q; void init(void){ no=0; memset(head,-1,sizeof head); } void addedge(int u,int v,int f,int cost){ edge[no].v=v; edge[no].f=f; edge[no].cost=cost; edge[no].next=head[u]; head[u]=no++; edge[no].v=u; edge[no].f=0; edge[no].cost=-cost; edge[no].next=head[v]; head[v]=no++; } int spfa(int s,int t){ int k,tp; memset(dis,0x3f,sizeof(dis)); memset(vis,0,sizeof(vis)); while(!q.empty()) q.pop(); q.push(s); dis[s]=0; vis[s]=1; while(!q.empty()){ tp=q.front(); q.pop(); vis[tp]=0; for(k=head[tp];k!=-1;k=edge[k].next) if(dis[edge[k].v]>dis[tp]+edge[k].cost&&edge[k].f){ dis[edge[k].v]=dis[tp]+edge[k].cost; pre[edge[k].v]=tp; rec[edge[k].v]=k; if(vis[edge[k].v]==0){ vis[edge[k].v]=1; q.push(edge[k].v); } } } if(dis[t]==INF) return 0; return 1; } pair<int, int> Mcmf(int s,int t){ int minflow,k,mincost=0,maxflow=0; while(spfa(s,t)){ k=t; minflow=INF; while(k!=s){ minflow=min(minflow,edge[rec[k]].f); k=pre[k]; } k=t; maxflow+=minflow; while(k!=s){ mincost+=minflow*edge[rec[k]].cost; edge[rec[k]].f-=minflow; edge[rec[k]^1].f+=minflow; k=pre[k]; } } return make_pair(maxflow,mincost); } int n; int x[MAX][10],y[MAX][10]; int main(){ int i,j,k,p; init(); scanf("%d",&n); for(i=1;i<=n;i++) for(j=0;j<4;j++) scanf("%d",&x[i][j]); for(i=1;i<=n;i++){ for(j=0;j<4;j++) scanf("%d",&y[i][j]); for(j=1;j<=n;j++){ int cnt=0; for(k=0;k<4;k++) for(p=0;p<4;p++) if(y[i][k]==x[j][p]) cnt++; addedge(j,i+ext,1,4-cnt); } } int s=0,t=200+ext; for(i=1;i<=n;i++){ addedge(s,i,1,0); addedge(i+ext,t,1,0); } pair<int,int> ans=Mcmf(s,t); printf("%d\n",ans.second); return 0; }
F: take
首先这道题可以算每个点对答案的贡献,考虑一下我翻出这张并且换的概率是什么,设dp[i]表示i拿到了钻石并且更换了一次的概率:那么dp[i]=p[i]*∑(dp[j]*q[j]) 0<=j<i且D[j]<D[i],这里q[j]表示从j到i从没发生一次更换的可能性,可以发现∑里面的东西可以用线段树维护出来
1 #include<cctype> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<algorithm> 6 7 #define maxn 100000+5 8 9 using namespace std; 10 11 typedef long long LL; 12 13 const int mod=998244353; 14 15 struct Seg_Tree { 16 int l, r; 17 int tag, sum; 18 }tr[maxn << 2]; 19 20 int p[maxn],d[maxn],c[maxn],dp[maxn]; 21 int n,cnt,tot,ans; 22 23 void Update(int k) 24 { 25 tr[k].sum = (tr[k << 1].sum + tr[k << 1 | 1].sum)%mod; 26 } 27 28 void Pushdown(int k) 29 { 30 if (tr[k].tag!=1) { 31 tr[k << 1].sum = (LL)tr[k<<1].sum*tr[k].tag%mod; 32 tr[k << 1 | 1].sum = (LL)tr[k<<1|1].sum*tr[k].tag%mod; 33 tr[k << 1].tag=(LL)tr[k<<1].tag*tr[k].tag%mod; 34 tr[k << 1 | 1].tag=(LL)tr[k<<1|1].tag*tr[k].tag%mod; 35 tr[k].tag = 1; 36 } 37 } 38 39 void Build(int k, int l, int r) 40 { 41 tr[k].l = l; tr[k].r = r; 42 if (l == r) { 43 tr[k].sum = 0; 44 tr[k].tag = 1; 45 return; 46 } 47 int mid = (l + r) >> 1; 48 Build(k << 1, l, mid); 49 Build(k << 1 | 1, mid + 1, r); 50 Update(k); 51 } 52 53 void Insert(int k,int pos,int val) 54 { 55 Pushdown(k); 56 if(tr[k].l==tr[k].r){ 57 tr[k].sum=(tr[k].sum+val)%mod; 58 return; 59 } 60 if(pos<=tr[k<<1].r) Insert(k<<1,pos,val); 61 else Insert(k<<1|1,pos,val); 62 Update(k); 63 } 64 65 void Modify(int k, int l, int r, int val) 66 { 67 Pushdown(k); 68 if (tr[k].l == l && tr[k].r == r) { 69 tr[k].sum = (LL)tr[k].sum*val%mod; 70 tr[k].tag = (LL)tr[k].tag*val%mod; 71 return; 72 } 73 if (tr[k << 1].r >= r) Modify(k << 1, l, r, val); 74 else if (tr[k << 1 | 1].l <= l) Modify(k << 1 | 1, l, r, val); 75 else Modify(k << 1, l, tr[k << 1].r, val), Modify(k << 1 | 1, tr[k << 1 | 1].l, r, val); 76 Update(k); 77 } 78 79 int Query(int k, int l, int r) 80 { 81 Pushdown(k); 82 if (tr[k].l == l && tr[k].r == r) return tr[k].sum; 83 if (tr[k << 1].r >= r) return Query(k << 1, l, r); 84 else if (tr[k << 1 | 1].l <= l) return Query(k << 1 | 1, l, r); 85 else return (Query(k << 1, l, tr[k << 1].r) + Query(k << 1 | 1, tr[k << 1 | 1].l, r))%mod; 86 } 87 88 int qpow(int a,int b) 89 { 90 int ans=1,base=a; 91 while(b){ 92 if(b&1) ans=(LL)ans*base%mod; 93 base=(LL)base*base%mod; 94 b>>=1; 95 } 96 return ans; 97 } 98 99 int bsearch(int x) 100 { 101 int l=1,r=tot,ans; 102 while(l<=r){ 103 int mid=(l+r)>>1; 104 if(c[mid]<=x) ans=mid,l=mid+1; 105 else r=mid-1; 106 } 107 return ans; 108 } 109 110 int main() 111 { 112 int n; 113 scanf("%d", &n); 114 c[++cnt]=0; 115 int inv=qpow(100,mod-2); 116 for (int i = 1; i <= n; i++){ 117 scanf("%d%d", &p[i],&d[i]); 118 c[++cnt]=d[i]; 119 p[i]=(LL)p[i]*inv%mod; 120 } 121 sort(c+1,c+1+cnt); c[0]=-1; 122 for(int i=1;i<=cnt;i++) 123 if(c[i]!=c[i-1]) c[++tot]=c[i]; 124 for(int i=1;i<=n;i++) 125 d[i]=bsearch(d[i]); 126 Build(1, 1, tot); 127 Insert(1,1,1); 128 129 for(int i=1;i<=n;i++){ 130 dp[i]=(LL)p[i]*Query(1,1,d[i]-1)%mod; 131 ans=(ans+dp[i])%mod; 132 Modify(1,1,d[i]-1,((1-p[i])%mod+mod)%mod); 133 Insert(1,d[i],dp[i]); 134 } 135 printf("%d",ans); 136 137 return 0; 138 }
G:max
取[1,n/c]中的最大两个就行,然后特别讨论一下n/c=1情况
#include <cstdio> #include <cstring> #include <queue> #include <algorithm> #include <cmath> #include <vector> #include <iostream> #include <stack> #include <set> #include <map> #define LSON l,m,x<<1 #define RSON m+1,r,x<<1|1 using namespace std; long long c,n; int main(){ scanf("%lld%lld",&c,&n); long long x=n/c; if(x<1){ puts("-1"); return 0; } if(x==1){ printf("%lld\n",c*c); return 0; } long long ans=x*(x-1); printf("%lld\n",ans*c*c); return 0; }
H:subseq
线段树从后向前维护以当前为起点的上升子序列的个数,再从前向后找第k个就好,爆longlong直接赋值INF无解
#include <cstdio> #include <cstring> #include <queue> #include <algorithm> #include <cmath> #include <vector> #include <stack> #include <set> #define LSON l,m,x<<1 #define RSON m+1,r,x<<1|1 using namespace std; const long long INF=1e18+5; const int MAX=5e5+5; int n,a[MAX]; long long big[MAX]; long long k,seg[MAX<<2]; vector<int> V; vector<int> ans; void update(int pos,long long val,int l,int r,int x){ if(l==r){ seg[x]+=val; if(seg[x]>INF) seg[x]=INF; return ; } int m=l+r>>1; if(pos<=m) update(pos,val,LSON); else update(pos,val,RSON); long long left=seg[x<<1],right=seg[x<<1|1]; if(left>INF||right>INF) seg[x]=INF; else seg[x]=left+right; if(seg[x]>INF) seg[x]=INF; } long long query(int L,int R,int l,int r,int x){ if(L>R) return 0; if(L<=l&&R>=r) return seg[x]; int m=l+r>>1; long long ans=0; if(R<=m) ans=query(L,R,LSON); else if(L>m) ans=query(L,R,RSON); else{ long long left=query(L,m,LSON),right=query(m+1,R,RSON); if(left>INF||right>INF) ans=INF; else ans=left+right; } if(ans>INF) ans=INF; return ans; } int main(){ int i; scanf("%d%lld",&n,&k); for(i=0;i<n;i++){ scanf("%d",&a[i]); V.push_back(a[i]); } sort(V.begin(),V.end()); V.erase(unique(V.begin(),V.end()),V.end()); for(i=n-1;i>=0;i--){ int id=lower_bound(V.begin(),V.end(),a[i])-V.begin()+1; long long rk=query(id+1,V.size(),1,V.size(),1); rk++; big[i]=rk; update(id,rk,1,V.size(),1); } int sta=0; for(i=0;i<n;i++){ if(sta>=a[i]) continue; if(k==0) break; if(big[i]>=k){ sta=a[i]; k--; ans.push_back(i+1); } else k-=big[i]; } if(k==0){ printf("%d\n",ans.size()); for(i=0;i<ans.size();i++) printf("%d ",ans[i]); } else puts("-1"); return 0; }
I:vcd
|S|=1时必选 |S|=2时选y坐标不相同的两个 |S|=3时要选摆成<形状的三个点 |S|>3时一定不可选
对于|S|=3的部分,可按x坐标排序后倒序处理,用线段树维护这些点的y坐标即可。
1 #include<queue> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<algorithm> 6 7 #define maxn 200000+5 8 9 using namespace std; 10 11 typedef long long LL; 12 13 const int mod=998244353; 14 15 struct Point{ 16 int x,y; 17 bool operator <(const Point &T)const{ 18 if(y==T.y) return x>T.x; 19 return y>T.y; 20 } 21 }p[maxn]; 22 23 struct Seg_Tree { 24 int l, r; 25 int sum; 26 }tr[maxn << 4]; 27 28 vector <int> s[maxn]; 29 30 int c[maxn<<1]; 31 int n,tot,cnt; 32 33 void Update(int k) 34 { 35 tr[k].sum = (tr[k << 1].sum + tr[k << 1 | 1].sum)%mod; 36 } 37 38 void Build(int k, int l, int r) 39 { 40 tr[k].l = l; tr[k].r = r; 41 if (l == r) { 42 tr[k].sum = 0; 43 return; 44 } 45 int mid = (l + r) >> 1; 46 Build(k << 1, l, mid); 47 Build(k << 1 | 1, mid + 1, r); 48 Update(k); 49 } 50 51 void Insert(int k,int pos) 52 { 53 if(tr[k].l==tr[k].r){ 54 tr[k].sum=(tr[k].sum+1)%mod; 55 return; 56 } 57 if(pos<=tr[k<<1].r) Insert(k<<1,pos); 58 else Insert(k<<1|1,pos); 59 Update(k); 60 } 61 62 int Query(int k, int l, int r) 63 { 64 if(l>r) return 0; 65 if (tr[k].l == l && tr[k].r == r) return tr[k].sum; 66 if (tr[k << 1].r >= r) return Query(k << 1, l, r); 67 else if (tr[k << 1 | 1].l <= l) return Query(k << 1 | 1, l, r); 68 else return (Query(k << 1, l, tr[k << 1].r) + Query(k << 1 | 1, tr[k << 1 | 1].l, r))%mod; 69 } 70 71 72 int bsearch(int x) 73 { 74 int l=1,r=tot,ans; 75 while(l<=r){ 76 int mid=(l+r)>>1; 77 if(c[mid]<=x) ans=mid,l=mid+1; 78 else r=mid-1; 79 } 80 return ans; 81 } 82 83 int main(){ 84 scanf("%d",&n); 85 for(int i=1;i<=n;i++) 86 scanf("%d%d",&p[i].x,&p[i].y),c[++cnt]=p[i].x,c[++cnt]=p[i].y; 87 sort(c+1,c+1+cnt); c[0]=-1; 88 for(int i=1;i<=cnt;i++) 89 if(c[i]!=c[i-1]) c[++tot]=c[i]; 90 for(int i=1;i<=n;i++) 91 p[i].x=bsearch(p[i].x),p[i].y=bsearch(p[i].y); 92 sort(p+1,p+1+n); 93 int ans=n,sum=0,tmp=0; 94 for(int i=1;i<=n;i++) 95 if(p[i].y!=p[i-1].y){ 96 ans=(ans+(LL)sum*tmp)%mod; 97 sum+=tmp; tmp=1; 98 } 99 else tmp++; 100 ans=(ans+(LL)sum*tmp)%mod; 101 Build(1,1,tot); 102 for(int i=1;i<=n;i++) 103 s[p[i].x].push_back(p[i].y); 104 for(int i=tot;i>=1;i--){ 105 int sz=s[i].size(); 106 for(int j=0;j<sz;j++){ 107 int tmp1=Query(1,1,s[i][j]-1); 108 int tmp2=Query(1,s[i][j]+1,tot); 109 ans=(ans+(LL)tmp1*tmp2)%mod; 110 } 111 for(int j=0;j<sz;j++) 112 Insert(1,s[i][j]); 113 } 114 printf("%d",ans); 115 return 0; 116 }
J:plan
先全选3或全选2,然后考虑用2和3补满或退1间重新选
#include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; typedef long long LL; long long n,p1,p2; long long ans; int main(){ scanf("%lld%lld%lld",&n,&p1,&p2); LL tmp1,tmp2; tmp1=(n/2)*p1; tmp2=(n/3)*p2; if(n%2==1){ LL tmp; tmp=min(p1,p2); if(tmp1) tmp=min(tmp,-p1+p2); tmp1+=tmp; } if(n%3==1){ LL tmp; tmp=min(p1,p2); if(tmp2) tmp=min(tmp,-p2+p1+p1); tmp2+=tmp; } else if(n%3==2){ LL tmp; tmp=min(p1,p2); tmp2+=tmp; } printf("%lld",min(tmp1,tmp2)); return 0; }