11.13 noip模拟试题
题目名称 笔记 括号 城堡
可执行文件名 note brackets castle
输入文件名 note.in brackets.in castle.in
输出文件名 note.in brackets.out castle.in
每个测试点时限 1 秒 1 秒 1 秒
内存限制 512MB 512MB 512MB
测试点数目 20 20 10
每个测试点分值 5 5 10
是否有部分分 否 否 否
题目类型 传统型 传统型 传统型
测试题 #4 笔记
笔记
【问题描述】
给定一个长度为?的序列?,下标编号为1~?。序列的每个元素都是1~?的
整数。定义序列的代价为
? ?+1 − ? ?
?−1
?=1
你现在可以选择两个数?和?,并将序列?中所有的?改成?。?可以与?相等。
请求出序列最小可能的代价。
【输入格式】
输入第一行包含两个整数?和?。第二行包含?个空格分隔的整数,代表序
列?。
【输出格式】
输出一行,包含一个整数,代表序列最小的代价。
【样例输入 1】
4 6
1 2 3 4 3 2
【样例输出 1】
3
【样例输入 2】
10 5
9 4 3 8 8
【样例输出 1】
6
【样例解释】
样例 1 中,最优策略为将 4 改成 3。样例 2 中,最优策略为将 9 改成 4。
测试题 #4 笔记
【数据规模和约定】
31。
60%的数据,?,? ≤ 2000。
对于100%的数据,1 ≤ ?,? ≤ 100,000。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/*暴力+小小的优化(没有啥大用)*/ #include<cstdio> #include<ctime> #include<algorithm> #define maxn 100010 using namespace std; int n,m,a[maxn],b[maxn],c[maxn],ans; int init(){ int x=0,f=1;char s=getchar(); while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();} while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();} return x*f; } int Abs(int x){ return x<0?-x:x; } int main() { freopen("note.in","r",stdin); freopen("note.out","w",stdout); n=init();m=init(); for(int i=1;i<=m;i++) a[i]=init(); if(n<=100){ for(int i=2;i<m;i++) if((a[i]<a[i-1]&&a[i]<a[i+1])||(a[i]>a[i-1]&&a[i]>a[i+1])) c[++c[0]]=a[i]; c[++c[0]]=a[1];c[++c[0]]=a[m]; } else for(int i=1;i<=n;i++)c[++c[0]]=a[i]; sort(c+1,c+1+c[0]); c[0]=unique(c+1,c+1+c[0])-c-1; for(int i=1;i<m;i++) ans=ans+Abs(a[i+1]-a[i]); for(int i=1;i<=c[0];i++) for(int j=1;j<=n;j++){ for(int k=1;k<=m;k++) if(a[k]==c[i])b[k]=j; else b[k]=a[k]; long long mx=0; for(int k=1;k<m;k++){ mx=mx+Abs(b[k+1]-b[k]); if(ans<mx)break; } if(ans>mx)ans=mx; //if(clock()>950){ // printf("%lld\n",ans);return 0; //} } printf("%lld\n",ans); fclose(stdin);fclose(stdout); return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* 嗯很好的思路 每个数字只与两边的有关 假设我们尝试着改 x 改成什么玩意是可以直接算出来的 先预处理处所有与x相邻的数们 那每个都要与x做差然后绝对值 如果我们队这一坨数排序的话 那么显然x改成中位数最优 然后枚举一下x 比较一下就好了 每个x都要sort一遍 这个并不会很慢 均摊还是很快的 */ #include<cstdio> #include<vector> #include<algorithm> #define maxn 100010 #define ll long long using namespace std; ll n,m,a[maxn],b[maxn]; ll sta,mx; vector<ll>G[maxn]; ll init(){ ll x=0,f=1;char s=getchar(); while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();} while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();} return x*f; } ll Abs(ll x){ return x<0?-x:x; } int main(){ freopen("note.in","r",stdin); freopen("note.out","w",stdout); n=init();m=init(); for(ll i=1;i<=m;i++)a[i]=init(); for(ll i=1;i<=m;i++) if(a[i]!=a[i-1])b[++b[0]]=a[i]; for(ll i=1;i<m;i++)sta+=Abs(a[i+1]-a[i]); m=b[0];b[0]=0; for(ll i=2;i<m;i++){ G[b[i]].push_back(b[i-1]); G[b[i]].push_back(b[i+1]); } if(b[2])G[b[1]].push_back(b[2]); if(b[m-1])G[b[m]].push_back(b[m-1]); for(ll i=1;i<=n;i++){ ll len=G[i].size(); if(!len)continue; sort(G[i].begin(),G[i].end()); ll mid=G[i][(len+1)/2-1]; ll pre=0,now=0; for(ll k=0;k<len;k++){ pre+=Abs(G[i][k]-i); now+=Abs(G[i][k]-mid); } mx=max(mx,pre-now); } printf("%I64d\n",sta-mx); return 0; }
测试题 #4 括号
括号
【问题描述】
有一个长度为?的括号序列,以及?种不同的括号。序列的每个位置上是哪
种括号是随机的,并且已知每个位置上出现每种左右括号的概率。求整个序列是
一个合法的括号序列的概率。
我们如下定义合法括号序列:
空序列是合法括号序列;
如果?是合法括号序列,那么???是合法括号序列,当且仅当?和?是同种
的左右括号;
如果?和?是合法括号序列,那么??是合法括号序列。
【输入格式】
输入第一行包含两个整数?和?。接下来的输入分为?组,每组?行。第?组第
?行包含两个实数?[?,?]和?[?,?],分别代表第?个位置上是第?类的左括号和右括号
的概率。
【输出格式】
输出一行,包含一个实数,代表序列是合法括号序列的概率。建议保留至少
5 位小数输出。只有当你的输出与标准答案之间的绝对误差不超过10 −5 时,才会
被判为正确。
【样例输入 1】
2 1
1.00000 0.00000
0.00000 1.00000
【样例输出 1】
1.00000
【样例输入 2】
4 1
0.50000 0.50000
1.00000 0.00000
0.00000 1.00000
0.50000 0.50000
测试题 #4 括号
【样例输出 2】
0.25000
【数据规模和约定】
对于20%的数据,? ≤ 50,? = 1,所有位置的概率非 0 即 1。
另外有 30%的数据,? ≤ 34,? = 1,前 10 个和后 10 个位置的所有概率都
是 0.5,中间剩余位置的概率非 0 即 1。
80%的数据,?,? ≤ 50。
对于100%的数据,1 ≤ ? ≤ 200,1 ≤ ? ≤ 50。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/*dpwa成了傻逼 还是交的暴力*/ #include<iostream> #include<cstdio> #define maxn 210 #define ld long double using namespace std; int n,k,s[maxn]; ld g[maxn][maxn][2],ans; void Dfs(int now,ld x){ if(now==n+1){ int falg=1; for(int i=1;i<=k;i++) if(s[i]){ falg=0;break; } if(falg)ans+=x;return; } for(int i=1;i<=k;i++){ if(s[i]-1>=0&&g[now][i][1]){ s[i]--;Dfs(now+1,x*g[now][i][1]);s[i]++; } if(s[i]+1<=n-now&&g[now][i][0]){ s[i]++;Dfs(now+1,x*g[now][i][0]);s[i]--; } } } int main() { freopen("brackets.in","r",stdin); freopen("brackets.out","w",stdout); cin>>n>>k; for(int i=1;i<=n;i++) for(int j=1;j<=k;j++) cin>>g[i][j][0]>>g[i][j][1]; if(n&1){ printf("0.00000\n");return 0; } Dfs(1,1);printf("%.5lf",double(ans)); fclose(stdin);fclose(stdout); return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/*维护两个数组 很好的解决了重复计算的问题*/ #include<iostream> #include<cstdio> #define ld long double #define maxn 210 using namespace std; int n,k; ld f[maxn][maxn],ff[maxn][maxn],g[maxn][maxn][2]; int main(){ freopen("brackets.in", "r", stdin); freopen("brackets.out", "w", stdout); cin>>n>>k; for(int i=1;i<=n;i++) for(int j=1;j<=k;j++) cin>>g[i][j][0]>>g[i][j][1]; for(int i=1;i<n;i++) for(int x=1;x<=k;x++) f[i][i+1]+=g[i][x][0]*g[i+1][x][1]; for(int i=n-1;i>=1;i--) for(int j=i+3;j<=n;j++){ for(int x=1;x<=k;x++) f[i][j]+=(f[i+1][j-1]+ff[i+1][j-1])*g[i][x][0]*g[j][x][1]; for(int x=i+1;x<j-1;x++) ff[i][j]+=(f[i][x]+ff[i][x])*f[x+1][j]; } printf("%.5f\n",(double)(f[1][n]+ff[1][n])); return 0; }
测试题 #4 城堡
城堡
【问题描述】
给定一张?个点?条边的无向连通图,每条边有边权。我们需要从?条边中
选出? − 1条, 构成一棵树。 记原图中从 1 号点到每个节点的最短路径长度为? ? ,
树中从 1 号点到每个节点的最短路径长度为? ? ,构出的树应当满足对于任意节点
?,都有? ? = ? ? 。
请你求出选出? − 1条边的方案数。
【输入格式】
输入的第一行包含两个整数?和?。
接下来?行,每行包含三个整数?、?和?,描述一条连接节点?和?且边权为
?的边。
【输出格式】
输出一行,包含一个整数,代表方案数对2 31 − 1取模得到的结果。
【样例输入】
3 3
1 2 2
1 3 1
2 3 1
【样例输出】
2
【数据规模和约定】
32 ≤ ? ≤ 5,? ≤ 10。
对于50%的数据,满足条件的方案数不超过 10000。
对于100%的数据,2≤ ? ≤ 1000,? − 1 ≤ ? ≤
? ?−1
2
,1 ≤ ? ≤ 100。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/*暴力 dfs+SPFA+Tarjan+dfs*/ #include<cstdio> #include<queue> #include<iostream> #include<cstring> #define maxn 1010 using namespace std; int n,m,num,head[maxn],f[maxn],D[maxn],S[maxn],topt,top; int s[maxn],vis[maxn],dfn[maxn],low[maxn],sum,Sum[maxn]; long long ans; struct node{ int u,v,t; }p[maxn]; struct edge{ int v,t,pre,x; }e[maxn*2]; queue<int>q; int init(){ int x=0,f=1;char s=getchar(); while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();} while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();} return x*f; } void Add(int from,int to,int dis,int i){ num++;e[num].v=to; e[num].x=i; e[num].t=dis; e[num].pre=head[from]; head[from]=num; } void Tarjan(int x,int fa){ dfn[x]=low[x]=++topt; s[++top]=x;vis[x]=1; for(int i=head[x];i;i=e[i].pre){ int v=e[i].v; if(v==fa||!f[e[i].x])continue; if(dfn[v]==0){ Tarjan(v,x);low[x]=min(low[x],low[v]); } else if(vis[v])low[x]=min(low[x],dfn[v]); } if(low[x]==dfn[x]){ sum++;while(x!=s[top]){ Sum[sum]++;vis[s[top]]=0;top--; } Sum[sum]++;vis[s[top]]=0;top--; } } bool Judge(){ memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(s,0,sizeof(s)); memset(vis,0,sizeof(vis)); memset(Sum,0,sizeof(Sum)); topt=top=sum=0; for(int i=1;i<=n;i++) if(dfn[i]==0)Tarjan(i,0); for(int i=1;i<=sum;i++) if(Sum[i]>1)return 0; return 1; } void SPFA(){ memset(D,127/3,sizeof(D)); D[1]=0;f[1]=1;q.push(1); while(!q.empty()){ int k=q.front();q.pop();f[k]=0; for(int i=head[k];i;i=e[i].pre){ int v=e[i].v; if(D[v]>D[k]+e[i].t){ D[v]=D[k]+e[i].t; if(f[v]==0){ f[v]=1;q.push(v); } } } } } void dfs(int now,int from,int x){ S[now]=min(S[now],x); for(int i=head[now];i;i=e[i].pre){ if(!f[e[i].x])continue; int v=e[i].v;if(v==from)continue; dfs(v,now,x+e[i].t); } } void Solve(){ memset(S,127/3,sizeof(S)); S[1]=0;dfs(1,1,0); for(int i=1;i<=n;i++) if(S[i]!=D[i])return; ans++;return; } void Dfs(int now){ if(now==m+1){ int cnt=0; for(int i=1;i<=m;i++) if(f[i])cnt++; if(cnt!=n-1||!Judge())return; Solve();return; } f[now]=1;Dfs(now+1); f[now]=0;Dfs(now+1); } int main() { freopen("castle.in","r",stdin); freopen("castle.out","w",stdout); n=init();m=init(); int u,v,t; for(int i=1;i<=m;i++){ u=init();v=init();t=init(); p[i].u=u;p[i].v=v;p[i].t=t; Add(u,v,t,i);Add(v,u,t,i); } SPFA();memset(f,0,sizeof(f));Dfs(1); cout<<ans<<endl; fclose(stdin);fclose(stdout); return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* 很机智的题解 维护每个点连出去的边中 有几个是最短路上的 假设 两个点 ij i有a条连边是最短路上的 j有b条 那么 1 i j 这三个点构成一棵树就有 1*a*b中方法 加上其他的点同理 最后乘法原理算一下 */ #include<cstdio> #include<queue> #include<iostream> #include<cstring> #define ll long long #define mod ((1<<31)-1) #define maxn 1010 using namespace std; int n,m,num,head[maxn],f[maxn],D[maxn],c[maxn]; ll ans=1; struct node{ int u,v,t; }p[maxn*maxn]; struct edge{ int v,t,pre; }e[maxn*2*maxn]; queue<int>q; int init(){ int x=0,f=1;char s=getchar(); while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();} while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();} return x*f; } void Add(int from,int to,int dis){ num++;e[num].v=to; e[num].t=dis; e[num].pre=head[from]; head[from]=num; } void SPFA(){ memset(D,127/3,sizeof(D)); D[1]=0;f[1]=1;q.push(1); while(!q.empty()){ int k=q.front();q.pop();f[k]=0; for(int i=head[k];i;i=e[i].pre){ int v=e[i].v; if(D[v]>D[k]+e[i].t){ D[v]=D[k]+e[i].t; if(f[v]==0){ f[v]=1;q.push(v); } } } } } int main() { freopen("castle.in","r",stdin); freopen("castle.out","w",stdout); n=init();m=init(); int u,v,t; for(int i=1;i<=m;i++){ u=init();v=init();t=init(); p[i].u=u;p[i].v=v;p[i].t=t; Add(u,v,t);Add(v,u,t); } SPFA(); for(int i=1;i<=m;i++){ if(D[p[i].v]==D[p[i].u]+p[i].t)c[p[i].v]++; if(D[p[i].u]==D[p[i].v]+p[i].t)c[p[i].u]++; } for(int i=2;i<=n;i++) ans=ans*(ll)c[i]%mod; cout<<ans<<endl; fclose(stdin);fclose(stdout); return 0; }