noip模拟61[恢复]
noip模拟61 solutions
怎么说考场上还是有一点点小小的思路,不过总是想叉了
该切掉的切不掉而且最近挂分非常严重,不是看不清题就是忘记输出
T1 交通
没想到吧可以直接并查集。。。。。
一开始以为是组合数,后来以为是容斥,再后来就不想写了
因为确定一条边之后就会一连串的确定好多边
具体来说就是对边进行连边,在同一个环里的,会唯一确定,也就是说每一个环只有两种方案
AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=1000005;
const int mod=998244353;
int n,ans;
int ksm(int x,int y){
int ret=1;
while(y){
if(y&1)ret=ret*x%mod;
x=x*x%mod;y>>=1;
}return ret;
}
bool vis[N];
int rd[N][2],cd[N][2];
int fa[N*2];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
signed main(){
#ifdef oj
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
scanf("%lld",&n);
fo(i,1,2*n){
fa[i]=i;
int x,y;scanf("%lld%lld",&x,&y);
if(cd[x][0])cd[x][1]=i;
else cd[x][0]=i;
if(rd[y][0])rd[y][1]=i;
else rd[y][0]=i;
}
fo(i,1,n){
int fx=find(cd[i][0]),fy=find(cd[i][1]);
if(fx!=fy)fa[fx]=fy;
fx=find(rd[i][0]);fy=find(rd[i][1]);
if(fx!=fy)fa[fx]=fy;
}
int bas=0;
fo(i,1,2*n){
if(!vis[find(i)])bas++,vis[find(i)]=true;
}
printf("%lld",ksm(2ll,bas));
}
T2 小P的单调数列
\(\mathcal{O(n^3)}\)的DP还是非常好想的,但是我那个定义无法用线段树优化
所以这个正解就是贪心!!!没想到吧啊
对于求平均数的这类题,当然是段数越小越好
但是由于第一段只能是上升的,所以最优策略就是一段上升或者一段上升+一段下降
直接维护最长上升序列就好了
AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=1e5+5;
int n,a[N],lsh[N],lh,rak[N];
double ans;
struct node{
#define ls x<<1
#define rs x<<1|1
int sum[N*4];
void pushup(int x){sum[x]=max(sum[ls],sum[rs]);}
void ins(int x,int l,int r,int pos,int v){
if(l==r)return sum[x]=max(sum[x],v),void();
int mid=l+r>>1;
if(pos<=mid)ins(ls,l,mid,pos,v);
else ins(rs,mid+1,r,pos,v);
pushup(x);
}
int query(int x,int l,int r,int ql,int qr){
if(ql>qr)return 0;
if(ql<=l&&r<=qr)return sum[x];
int mid=l+r>>1,ret=0;
if(ql<=mid)ret=max(ret,query(ls,l,mid,ql,qr));
if(qr>mid)ret=max(ret,query(rs,mid+1,r,ql,qr));
return ret;
}
#undef ls
#undef rs
}xds;
int f[N],g[N];
signed main(){
#ifdef oj
freopen("b.in","r",stdin);
freopen("b.out","w",stdout);
#endif
scanf("%lld",&n);
fo(i,1,n)scanf("%lld",&a[i]),lsh[++lh]=a[i];
sort(lsh+1,lsh+n+1);
lh=unique(lsh+1,lsh+n+1)-lsh-1;
fo(i,1,n){
rak[i]=lower_bound(lsh+1,lsh+n+1,a[i])-lsh;
int mx=xds.query(1,1,lh,1,rak[i]-1);
f[i]=mx+a[i];
xds.ins(1,1,lh,rak[i],f[i]);
}
memset(xds.sum,0,sizeof(xds.sum));
fu(i,n,1){
int mx=xds.query(1,1,lh,1,rak[i]-1);
g[i]=mx+a[i];
xds.ins(1,1,lh,rak[i],g[i]);
}
fu(i,n,1)g[i]=max(g[i],g[i+1]);
fo(i,1,n){
//cout<<f[i]<<" "<<g[i]<<endl;
ans=max(ans,1.0*f[i]);
ans=max(ans,1.0*(f[i]+g[i+1])/2.0);
}
printf("%.3lf",ans+1e-12);
}
T3 矩阵
这个题是我第一次在考试中遇到构造题
我考场上只会只有行和列的,并且还没有输出步数
首先,我们知道这个对角线是用来干啥的,用来平衡你那些不相等的行和列
所以我们直接对前两行操作,先操作第一行,让主对角线的前两个数相等
然后一个一个主对角线的操作,一直到最后一个就可以直接消掉了
列是一样的,因为此时你已经把每一行每一列每一个对角线都操作过了,如果没有成功,直接-1
要么就是直接输出
AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=1005;
const int inf=0x3f3f3f3f;
int n,m,a[N][N];
int cz[N*6][3],cnt;
signed main(){
#ifdef oj
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
#endif
scanf("%lld%lld",&n,&m);
fo(i,1,n)fo(j,1,m)scanf("%lld",&a[i][j]);
cz[++cnt][0]=1;cz[cnt][1]=1;cz[cnt][2]=a[2][2]-a[1][1];
fo(i,1,m)a[1][i]+=cz[cnt][2];
cz[++cnt][0]=3;cz[cnt][1]=0;cz[cnt][2]=-a[1][1];
fo(i,1,min(n,m))a[i][i+0]+=cz[cnt][2];
fo(i,2,m-1){
cz[++cnt][0]=3;cz[cnt][1]=i-1;cz[cnt][2]=a[2][i]-a[1][i];
fo(j,1,min(n,m-cz[cnt][1]))a[j][j+cz[cnt][1]]+=cz[cnt][2];
}
cz[++cnt][0]=3;cz[cnt][1]=m-1;cz[cnt][2]=a[2][m]-a[1][m];a[1][m]=a[2][m];
fo(i,1,m)if(a[1][i]){
cz[++cnt][0]=2;cz[cnt][1]=i;cz[cnt][2]=-a[1][i];
fo(j,1,n)a[j][i]+=cz[cnt][2];
}
fo(i,2,n-1){
cz[++cnt][0]=3;cz[cnt][1]=1-i;cz[cnt][2]=a[i][2]-a[i][1];
fo(j,i,min(n,m-cz[cnt][1]))a[j][j+cz[cnt][1]]+=cz[cnt][2];
}
cz[++cnt][0]=3;cz[cnt][1]=1-n;cz[cnt][2]=a[n][2]-a[n][1];a[n][1]=a[n][2];
fo(i,1,n)if(a[i][1]){
cz[++cnt][0]=1;cz[cnt][1]=i;cz[cnt][2]=-a[i][1];
fo(j,1,m)a[i][j]+=cz[cnt][2];
}
bool flag=true;
fo(i,1,n)fo(j,1,m)if(a[i][j])flag=false;
if(flag){
printf("%lld\n",cnt);
fo(i,1,cnt)printf("%lld %lld %lld\n",cz[i][0],cz[i][1],cz[i][2]);
}
else printf("-1");
}
T4 花瓶
其实我在考场上就想到斜率优化了
但是因为前缀和无序就放弃了,还白白浪费我1h
无序就直接排序啊,然后你就有了\(\mathcal{O(n^2logn)}\)的做法
然后你发现我可以用前面更新后面,然后就可以直接扫单调队列了,
AC_code
#include<bits/stdc++.h>
using namespace std;
#define oj
#define int long long
#define fo(i,x,y) for(int i=(x);i<=(y);i++)
#define fu(i,x,y) for(int i=(x);i>=(y);i--)
const int N=5005;
const int inf=0x3f3f3f3f3f3f3f3f;
int n,a[N],pre[N];
int f[N][N],ans=-inf;
int sta[N][N],bot[N],top[N];
struct node{
int va,id;
bool operator < (node a)const{
return va>a.va;
}
}ji[N];
int rak[N],sum[N],rk[N],wo[N];
double get(int i,int x,int y){
//if(pre[x]==pre[y])return
return 1.0*(f[i][x]-f[i][y])/(1.0*pre[x]-pre[y]);
}
signed main(){
#ifdef oj
freopen("d.in","r",stdin);
freopen("d.out","w",stdout);
#endif
scanf("%lld",&n);
fo(i,1,n)scanf("%lld",&a[i]),pre[i]=pre[i-1]+a[i];
fo(i,0,n)ji[i].va=pre[i],ji[i].id=i;
sort(ji+0,ji+n+1);
fo(i,0,n)rak[ji[i].id]=i;
//fo(i,1,n)cout<<pre[i]<<" ";cout<<endl;
memset(f,-0x3f,sizeof(f));
fo(i,1,n)f[i][0]=0;
fo(i,1,n){
bot[i]=1;top[i]=0;
memset(sum,0,sizeof(sum));
fo(j,0,i-1)sum[rak[j]]++;
fu(j,n,0)sum[j]+=sum[j+1];
fu(j,i-1,0)rk[j]=sum[rak[j]],wo[rk[j]]=j;
fo(w,1,i){
int j=wo[w];//cout<<j<<" "<<pre[j]<<" "<<f[i][j]<<endl;
if(top[i]>=bot[i]&&pre[j]==pre[sta[i][top[i]]]){
if(f[i][j]>=f[i][sta[i][top[i]]])top[i]--;
else continue;
//continue;
}
//cout<<"bot & top "<<bot[i]<<" "<<top[i]<<endl;
while(bot[i]<top[i]&&get(i,sta[i][top[i]-1],sta[i][top[i]])<=get(i,sta[i][top[i]],j))
top[i]--;//cout<<"xl "<<get(i,sta[i][top[i]-1],sta[i][top[i]])<<" "<<get(i,sta[i][top[i]],j)<<endl;
sta[i][++top[i]]=j;
}
//cout<<endl<<"sta"<<" ";
//fo(j,bot[i],top[i])cout<<sta[i][j]<<" ";
//cout<<endl;
memset(sum,0,sizeof(sum));
fo(j,i+1,n)sum[rak[j]]++;
fo(j,1,n)sum[j]+=sum[j-1];
fu(j,n,i+1)rk[j]=sum[rak[j]]--,wo[rk[j]]=j;
fo(w,1,n-i){
int k=wo[w];//cout<<pre[k]<<" ";
while(bot[i]<top[i]&&get(i,sta[i][bot[i]],sta[i][bot[i]+1])>=1.0*(pre[k]-pre[i]))bot[i]++;
f[k][i]=f[i][sta[i][bot[i]]]+(pre[k]-pre[i])*(pre[i]-pre[sta[i][bot[i]]]);
//cout<<k<<" "<<i<<" "<<sta[i][bot[i]]<<" "<<f[k][i]<<" "<<f[i][sta[i][bot[i]]]<<" "<<pre[k]-pre[i]<<" "<<pre[i]-pre[sta[i][bot[i]]]<<endl;
}
//cout<<endl<<endl;
//fo(j,0,i-1)cout<<"F"<<j<<" "<<f[i][j]<<endl;
//cout<<endl;
}
fo(i,0,n)ans=max(ans,f[n][i]);
printf("%lld",ans);
}
QQ:2953174821