QAQ 又是一套水题集合
然后忧伤的故事是老师把时间调到了四个小时半
我又因为想要出道题花了半个小时写了一些其他的东西
然后最后没有写完题!QAQ
不然第三题可能多拿点分
上午的时候把所有题目的正解都想了出来
唯一美中不足的是自己想的第三题是O(n^4*300)的是时间复杂度
实际上离散化区间之后只会有n个区间,时间复杂度就是O(n^5)了QAQ
先说题解把
第一题 决战圆锥曲线
显然给定你的那个函数a*x+b*y+c*x*y对于x,y是相对等价的
又因为题目的输入具有随机性,显然可以通过维护线段树在线段树上做kd_tree的查询来完成
注意到x,y都是正整数,而且x的范围就区间的范围,所以我们利用线段树维护y的最大值就可以完成类似kd_tree的查询了
时间复杂度略微有些玄学,听说随机数据下O(nlog^2n)?
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=100010;
const LL oo=1LL<<60;
int n,m,x,p,v,le,re;
int A,B,C;
int a[maxn];
int mx[maxn<<2];
int mn[maxn<<2];
bool vis[maxn<<2];
LL ans;
int Get_R(){
x=(1LL*100000005*x+20150609)%998244353;
return x/100;
}
void read(int &num){
num=0;char ch=getchar();
while(ch<'!')ch=getchar();
while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
}
void build(int o,int L,int R){
if(L==R){
mx[o]=mn[o]=a[L];
return;
}
int mid=(L+R)>>1;
build(o<<1,L,mid);
build(o<<1|1,mid+1,R);
mx[o]=max(mx[o<<1],mx[o<<1|1]);
mn[o]=min(mn[o<<1],mn[o<<1|1]);
}
void down(int o,int l,int r){
int Mn,Mx;
vis[l]^=1;vis[r]^=1;
Mn=mn[l];Mx=mx[l];
mx[l]=100000-Mn;mn[l]=100000-Mx;
Mn=mn[r];Mx=mx[r];
mx[r]=100000-Mn;mn[r]=100000-Mx;
vis[o]=false;
}
void UPD(int o,int L,int R){
if(L==R){mx[o]=mn[o]=v;return;}
int mid=(L+R)>>1;
int l=(o<<1),r=(l|1);
if(vis[o])down(o,l,r);
if(p<=mid)UPD(l,L,mid);
else UPD(r,mid+1,R);
mx[o]=max(mx[l],mx[r]);
mn[o]=min(mn[l],mn[r]);
}
void modify(int o,int L,int R){
if(L>=le&&R<=re){
vis[o]^=1;
int Mn=mn[o],Mx=mx[o];
mx[o]=100000-Mn;
mn[o]=100000-Mx;
return;
}
int mid=(L+R)>>1;
int l=(o<<1),r=(l|1);
if(vis[o])down(o,l,r);
if(re<=mid)modify(l,L,mid);
else if(le>mid)modify(r,mid+1,R);
else modify(l,L,mid),modify(r,mid+1,R);
mx[o]=max(mx[l],mx[r]);
mn[o]=min(mn[l],mn[r]);
}
LL Get_ans(int o,int L,int R){
int tmp=mx[o];LL k=(A+1LL*C*tmp);
return k*R+1LL*B*tmp;
}
void kd_ask(int o,int L,int R){
if(L==R){
ans=max(ans,1LL*A*L+1LL*B*mn[o]+1LL*C*L*mn[o]);
return;
}
int mid=(L+R)>>1;
int l=(o<<1),r=(l|1);
if(vis[o])down(o,l,r);
LL d1=Get_ans(l,L,mid);
LL d2=Get_ans(r,mid+1,R);
if(d1>d2){
if(d1>ans)kd_ask(l,L,mid);
if(d2>ans)kd_ask(r,mid+1,R);
}else{
if(d2>ans)kd_ask(r,mid+1,R);
if(d1>ans)kd_ask(l,L,mid);
}return;
}
void ask(int o,int L,int R){
if(L>=le&&R<=re){
kd_ask(o,L,R);
return;
}
int mid=(L+R)>>1;
int l=(o<<1),r=(l|1);
if(vis[o])down(o,l,r);
if(re<=mid)ask(l,L,mid);
else if(le>mid)ask(r,mid+1,R);
else ask(l,L,mid),ask(r,mid+1,R);
}
int main(){
freopen("conic.in","r",stdin);
freopen("conic.out","w",stdout);
read(n);read(m);read(x);
for(int i=1;i<=n;++i)a[i]=Get_R()%100001;
build(1,1,n);
while(m--){
char ch=getchar();
while(ch<'!')ch=getchar();
if(ch=='C'){
p=Get_R()%n+1;v=Get_R()%100001;
UPD(1,1,n);
}else if(ch=='R'){
le=Get_R()%n+1;re=Get_R()%n+1;
if(le>re)swap(le,re);
modify(1,1,n);
}else{
read(A);read(B);read(C);
ans=-oo;
le=Get_R()%n+1;re=Get_R()%n+1;
if(le>re)swap(le,re);
ask(1,1,n);
printf("%lld\n",ans);
}
}return 0;
}
第二题
显然有30分是白送给状态压缩或者插头DP的
之前有一道题目是最小割我用插头DP骗了90分,那么考虑这道题能不能用网络流解决
不难发现如果两个B可以消掉,这两个B的行的奇偶性一定不一样
这样对于B来说具有二分图的性质,但是还有考虑A的影响
而且我们的每个流都要表示一个B-A-B的方案
仔细思考后可以得到如下建图:
我们的S向行数是偶数的B连边,行数是奇数的B向T连边
对于行数是偶数的B我们向四个方向的A连边
对于每个A拆点限流
之后行数是偶数的A向左右两个方向的B连边
行数是奇数的A向上下两个方向的B连边
不难发现每个流都代表一个可行的L
那么求最大流即可,由于边权都是1,所以网络流跑的飞快
QAQ考试的时候心虚还写了个插头DP对拍QAQ
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<queue>
using namespace std;
const int maxn=500010;
const int oo=0x7fffffff;
int n,m,S,T,ans;
int c[510][510];
int idx[510][510],tot=0;
int h[maxn],cnt=1;
int vis[maxn];
struct edge{
int to,next,w;
}G[8000010];
queue<int>Q;
void add(int x,int y,int z){
++cnt;G[cnt].to=y;G[cnt].next=h[x];G[cnt].w=z;h[x]=cnt;
++cnt;G[cnt].to=x;G[cnt].next=h[y];G[cnt].w=0;h[y]=cnt;
}
void read(int &num){
num=0;char ch=getchar();
while(ch<'!')ch=getchar();
while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
}
bool BFS(){
Q.push(S);
for(int i=S;i<=T;++i)vis[i]=-1;
vis[S]=0;
while(!Q.empty()){
int u=Q.front();Q.pop();
for(int i=h[u];i;i=G[i].next){
int v=G[i].to;
if(vis[v]==-1&&G[i].w>0){
vis[v]=vis[u]+1;
Q.push(v);
}
}
}return vis[T]!=-1;
}
int DFS(int x,int f){
if(x==T||f==0)return f;
int w,used=0;
for(int i=h[x];i;i=G[i].next){
if(vis[G[i].to]==vis[x]+1){
w=f-used;
w=DFS(G[i].to,min(w,G[i].w));
G[i].w-=w;G[i^1].w+=w;
used+=w;if(used==f)return used;
}
}
if(!used)vis[x]=-1;
return used;
}
void dinic(){
ans=0;
while(BFS())ans+=DFS(S,oo);
}
int main(){
freopen("molecule.in","r",stdin);
freopen("molecule.out","w",stdout);
read(n);read(m);
for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)read(c[i][j]);
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
if(c[i][j]==0)continue;
idx[i][j]=++tot;
if(c[i][j]==1)++tot;
}
}S=0;T=tot+1;
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
if(c[i][j]==0)continue;
if(i&1){
if(c[i][j]==2){
add(S,idx[i][j],1);
if(j-1>=1&&c[i][j-1]==1)add(idx[i][j],idx[i][j-1],1);
if(j+1<=m&&c[i][j+1]==1)add(idx[i][j],idx[i][j+1],1);
if(i-1>=1&&c[i-1][j]==1)add(idx[i][j],idx[i-1][j],1);
if(i+1<=n&&c[i+1][j]==1)add(idx[i][j],idx[i+1][j],1);
}else{
add(idx[i][j],idx[i][j]+1,1);
if(i-1>=1&&c[i-1][j]==2)add(idx[i][j]+1,idx[i-1][j],1);
if(i+1<=n&&c[i+1][j]==2)add(idx[i][j]+1,idx[i+1][j],1);
}
}else{
if(c[i][j]==2)add(idx[i][j],T,1);
else{
add(idx[i][j],idx[i][j]+1,1);
if(j+1<=m&&c[i][j+1]==2)add(idx[i][j]+1,idx[i][j+1],1);
if(j-1>=1&&c[i][j-1]==2)add(idx[i][j]+1,idx[i][j-1],1);
}
}
}
}dinic();
printf("%d\n",ans);
return 0;
}
第三题
我们注意到不能单独计算i比j强的概率并装包
因为概率之间会相互影响
所以我们不妨枚举每个点在那个区间里
不难发现其他点对于这个点只会有三种情况
win,lose,in(在区间里)
不妨设f(i,j)表示有i个点比当前点强且有j个点和当前点在一个区间里
转移的时候分情况装包就可以了
之后我们注意到对于一个状态f(i,j),当前点的排名可能是i+1->i+j+1
而且概率都是f(i,j)*P/(j+1) P是当前点在这个区间的概率
然后贡献给Ans(i)就可以了
时间复杂度O(n^5),考试的时候时间不够没有调出来(现在貌似被OJ卡了常数?
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#define eps 1e-10
#define fastcall __attribute__((optimize("-O3")))
#define IL __inline__ __attribute__((always_inline))
using namespace std;
const int maxn=162;
int n,cnt=0;
int d[maxn];
struct OP{
int L,R;
}c[maxn];
double g[maxn];
double f[82][82];
fastcall IL int Get_pos(int v){
int L=1,R=cnt;
while(L<R){
int mid=L+((R-L+1)>>1);
if(d[mid]<=v)L=mid;
else R=mid-1;
}return L;
}
double Min(double L,double R){return L<R?L:R;}
double Max(double L,double R){return L>R?L:R;}
fastcall IL double Get_win(int L,int R,int i){
if(c[i].L>=L)return 0;
return (double)(Min(c[i].R,L)-c[i].L)/(c[i].R-c[i].L);
}
fastcall IL double Get_lose(int L,int R,int i){
if(c[i].R<=R)return 0;
return (double)(c[i].R-Max(c[i].L,R))/(c[i].R-c[i].L);
}
fastcall IL double Get_in(int L,int R,int i){
if(c[i].L>=R||c[i].R<=L)return 0;
return (double)(Min(c[i].R,R)-Max(c[i].L,L))/(c[i].R-c[i].L);
}
fastcall IL void Get_f(int L,int R,int now){
double P=(double)(R-L)/(c[now].R-c[now].L);
memset(f,0,sizeof(f));f[0][0]=1;
for(int i=1;i<=n;++i){
if(i==now)continue;
double p1=Get_win(L,R,i);
double p2=Get_lose(L,R,i);
double p3=Get_in(L,R,i);
for(int j=i;j>=0;--j){
for(int k=i-j;k>=0;--k){
f[j][k]=f[j][k]*p1;
if(j>=1)f[j][k]=f[j][k]+f[j-1][k]*p2;
if(k>=1)f[j][k]=f[j][k]+f[j][k-1]*p3;
}
}
}
for(int i=0;i<n;++i){
for(int j=0;j<n-i;++j){
double tmp=f[i][j];
if(fabs(tmp)<eps)continue;
tmp=tmp*P/(j+1);
for(int k=0;k<=j;++k){
g[i+k]=g[i+k]+tmp;
}
}
}return;
}
int main(){
freopen("gg.in","r",stdin);
freopen("gg.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d%d",&c[i].L,&c[i].R);
d[++cnt]=c[i].L;d[++cnt]=c[i].R;
}sort(d+1,d+cnt+1);
cnt=unique(d+1,d+cnt+1)-d-1;
for(int i=1;i<=n;++i){
memset(g,0,sizeof(g));
int L=Get_pos(c[i].L),R=Get_pos(c[i].R);
for(int j=L;j<R;++j)Get_f(d[j],d[j+1],i);
for(int j=0;j<n;++j)printf("%.6lf ",g[j]);
printf("\n");
}return 0;
}
最近两天的题目没什么好说的QAQ
都是很基础的东西,推一推就可以写了
基本上拍过了就A了QAQ
概率还是弱,如果上午一遍写对的话就AK了
有时间补补概率。。