noip模拟赛Bywzj52501 17.10.18
T1 rob
环形消灭虫子
先想出了一个n^2暴力
然后我们想到 如果从两个连续的点求解 则会出现仅有的两种结果
(因为这两种情况的交是全集)
当时因为Naive求了50次
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #include<cstdlib> #define ll long long using namespace std; int a[2*152600]; ll dp[2*152600]; int main() { freopen("rob.in","r",stdin); freopen("rob.out","w",stdout); int n; scanf("%d",&n); for(int i=1;i<=n;i++){scanf("%d",&a[i]);a[i+n]=a[i];} ll res=-1; int tms=min(n,50); for(int i=0;i<tms;i++) { memset(dp,0,sizeof(dp)); for(int j=1;j<=n;j++) { if(j>2)dp[j]=dp[j-2]; if(j>=3 && dp[j]<dp[j-3])dp[j]=dp[j-3]; dp[j]+=a[j+i]; } dp[n]=max(dp[n-1],dp[n-2]); res=max(res,dp[n]); } printf("%lld",res); return 0; }
T2 destroy
有n个点m条边
现在删掉一些边 把图分为两个联通块 求两个联通块中权值之和最小值
我们跑一个最小生成树 再把其中最大的一条边砍掉 就可以保证 剩下的是两个联通块 且权值之和最小
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #include<cstdlib> #define ll long long using namespace std; inline int read() { char ch=getchar(); int x=0,f=1; while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){x=10*x+ch-'0';ch=getchar();} return x*f; } int n,m; struct edg { int from,to; ll val; bool operator <(const edg b)const { return val<b.val; } }es[152600]; int cnt; int fa[152600]; inline int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);} inline void add(int u,int v,int w) { es[cnt++]=(edg){u,v,w}; } ll mx,sm; void kruskal() { sort(es,es+cnt); for(int i=0;i<=cnt;i++)fa[i]=i; for(int i=0;i<cnt;i++) { int fx=find(es[i].from); int fy=find(es[i].to); if(fx!=fy) { mx=max(mx,es[i].val); sm+=es[i].val; fa[fx]=fy; } } } int main() { freopen("destroy.in","r",stdin); freopen("destroy.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); add(u,v,w); } kruskal(); printf("%lld",sm-mx); return 0; }
T3 permutation
给定n个数 求这n个数全排列中满足
对所有 2 ≤ i ≤ N - 1,都有 A[i - 1] + A[i + 1] ≥ 2 × A[i]
的方案数
50分 全排列
70分 状压 n^3*2^n
100分 爆搜
爆搜。。。
但是LZJ巨神告诉我们 事情并没有这么简单
首先 满足条件的序列肯定是下凸的单峰型
首先考虑序列
i j ... ... l k
现在要在这个序列左端插入一个x 只要满足 a[x]-a[j]>=2*a[i]就可以插进来 j l之间是什么数并不影响结果
右端插x同理
于是我们排序 枚举每个数往左插还是往右插
复杂度n^5
#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #include<cstdlib> #define ll long long const int MOD=998244353; using namespace std; inline int read() { char ch=getchar(); int x=0,f=1; while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){x=10*x+ch-'0';ch=getchar();} return x*f; } int a[50]; int n; ll ans; int dp[50][50][50][50]; bool vis[50][50][50][50]; int cnt[50]; void dfs(int i,int j,int k,int l) { if(vis[i][j][k][l])return; vis[i][j][k][l]=1; int mx=max(i,max(j,max(k,l))); mx++; if(mx==n) { if((!j||a[j]-a[i]>=a[i]-a[mx]) && (!l||a[l]-a[k]>=a[k]-a[mx])) (ans+=dp[i][j][k][l])%=MOD; return; } for(int o=0;o<=cnt[mx];o++) { bool flag1=0,flag2=0; if(!j||a[mx]+a[j]>=2*a[i]||!o)flag1=1; if(!l||a[mx]+a[l]>=2*a[k]||!(cnt[mx]-o))flag2=1; if(flag1 && flag2) { int ni=i,nj=j,nk=k,nl=l; if(o==1)nj=i,ni=mx; else if(o>1)nj=ni=mx; if(cnt[mx]-o==1)nl=k,nk=mx; else if(cnt[mx]-o>1)nl=nk=mx; (dp[ni][nj][nk][nl]+=dp[i][j][k][l])%=MOD; } } } int main() { //freopen("permutation.in","r",stdin); //freopen("permutation.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;i++)a[i]=-read(); sort(a+1,a+n+1); int tag=0; for(int i=1;i<=n;i++)a[i]=-a[i]; for(int i=1;i<=n;i++) { if(a[i]==a[tag])cnt[tag]++; else { a[++tag]=a[i]; cnt[tag]=1; } } n=tag; dp[0][0][0][0]=1; for(int i=0;i<n;i++) for(int j=0;j<=i;j++) for(int k=0;k<=i;k++) for(int l=0;l<=k;l++) { dfs(i,j,k,l); dfs(k,l,i,j); } for(int i=1;i<=n;i++) for(int j=1;j<=cnt[i];j++)(ans*=j)%=MOD; cout<<ans; }
Orz LZJ
最后得分应该是80+100+50=230
(第一题爆int咯妈了个zz)
但是xgy错了一个价值80分的等号
yyc错了价值...不知道多少分反正好多好多分的三个字符
所以我好像是3个250中考的最高的Orz