完美的序列(sequence)
Time Limit:1000ms Memory Limit:64MB
题目描述
LYK 认为一个完美的序列要满足这样的条件:对于任意两个位置上的数都不相同。然而
并不是所有的序列都满足这样的条件。
于是 LYK 想将序列上的每一个元素都增加一些数字(当然也可以选择不增加),使得整个
序列变成美妙的序列。
具体地, LYK 可以花费 1 点代价将第 i 个位置上的数增加 1,现在 LYK 想花费最小的代价
使得将这个序列变成完美的序列。
输入格式(sequence.in)
第一行一个数 n,表示数字个数。
接下来一行 n 个数 ai 表示 LYK 得到的序列。
输出格式(sequence.out)
一个数表示变成完美的序列的最小代价。
输入样例
4
1 1 3 2
输出样例
3
数据范围
对于 30%的数据 n<=5。
对于 60%的数据 n<=1000。
对于 80%的数据 n<=30000, ai<=3000。
对于 100%的数据 n<=100000, 1<=ai<=100000。
/* 要让序列变成一个不含相同元素的 首先排序 排完序后只需让序列变成一个严格上升的序列 现在让a[i]=a[i]-i 这样处理后只需让序列变成一个不降序列就保证了原序列是上升序列 (因为a[i]比a[i-1]多减了1,只需a[i]不比a[i-1]小即可) */ #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxn 100010 using namespace std; int n,ans; int a[maxn]; int init() { int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } int main() { freopen("sequence.in","r",stdin); freopen("sequence.out","w",stdout); n=init(); for(int i=1;i<=n;i++) a[i]=init(); sort(a+1,a+n+1); for(int i=1;i<=n;i++) a[i]-=i; for(int i=2;i<=n;i++) { if(a[i]>=a[i-1])continue; ans+=a[i-1]-a[i]; a[i]=a[i-1]; } printf("%d ",ans); return 0; }
LYK 与实验室(lab)
Time Limit:5000ms Memory Limit:64MB
题目描述
LYK 在一幢大楼里,这幢大楼共有 n 层, LYK 初始时在第 a 层上。
这幢大楼有一个秘密实验室,在第 b 层,这个实验室非常特别,对 LYK 具有约束作用,
即若 LYK 当前处于 x 层,当它下一步想到达 y 层时,必须满足|x-y|<|x-b|,而且由于实验室
是不对外开放的,电梯无法停留在第 b 层。
LYK 想做一次旅行,即它想按 k 次电梯,它想知道不同的旅行方案个数有多少个。
两个旅行方案不同当前仅当存在某一次按下电梯后停留的楼层不同。
输入格式(lab.in)
一行 4 个数, n,a,b,k。
输出格式(lab.out)
一个数表示答案,由于答案较大,将答案对 1000000007 取模后输出。
输入样例 1
5 2 4 1
输出样例 1
2
输入样例 2
5 2 4 2
输出样例 2
2
输入样例 3
5 3 4 1
输出样例 3
0
数据范围
对于 20%的数据 n,k<=5。
对于 40%的数据 n,k<=10。
对于 60%的数据 n,k<=500。
对于 90%的数据 n,k<=2000。
对于 100%的数据 n,k<=5000。
/* dp O(n^3) 超时 */ #include<iostream> #include<cstdio> #include<cstring> #define mod 1000000007 #define maxn 5010 using namespace std; int n,w,a,b,ans; int dp[2][maxn]; int init() { int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } int abs(int x) { return x>=0?x:-x; } int main() { freopen("lab.in","r",stdin); freopen("lab.out","w",stdout); n=init();a=init();b=init();w=init(); for(int i=1;i<=n;i++)dp[w+1&1][i]=1; for(int i=w;i>=1;i--) { for(int j=1;j<=n;j++) dp[i&1][j]=0; for(int j=1;j<=n;j++) { for(int k=1;k<=n;k++) { if(k==b||k==j)continue; if(abs(j-k)<abs(j-b)) dp[i&1][j]=(dp[i&1][j]+dp[i+1&1][k])%mod; } } } printf("%d ",dp[1&1][a]); return 0; }
/* 搜索-->记忆化搜索-->dp O(n^3)-->dp O(n^2) dp状态 dp[i][j]表示第i次停在j的方案数 倒推 边界为dp[w+1][j]=1(j!=b) 因为最后可以停在除了b的所有地方 dp[i][j]+=dp[i+1][k](abs(j-k)<abs(j-b)) 本来应该循环但超时 令x=abs(j-b) abs(j-k)<x -x<j-k<x j-x<k<j+x 维护i+1的前缀和可以O(1)查出dp[i+1][j-x]到dp[i+1][j+x]得和 注意不能停在原楼层 */ #include<iostream> #include<cstdio> #include<cstring> #define mod 1000000007 #define maxn 5010 using namespace std; int n,w,a,b,ans; int dp[2][maxn]; int sum[maxn]; int init() { int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } int abs(int x) { return x>=0?x:-x; } int main() { freopen("lab.in","r",stdin); freopen("lab.out","w",stdout); n=init();a=init();b=init();w=init(); for(int i=1;i<=n;i++) { if(i!=b)dp[w+1&1][i]=1; sum[i]=(sum[i-1]+dp[w+1&1][i])%mod; } for(int i=w;i>=1;i--) { for(int j=1;j<=n;j++) dp[i&1][j]=0; for(int j=1;j<=n;j++) { if(j==b)continue; int cha=abs(j-b)-1;int l=max(0,j-cha),r=min(n,j+cha); if(l>r)continue; dp[i&1][j]=((sum[r]-sum[l-1])%mod+mod)%mod; dp[i&1][j]=((dp[i&1][j]-dp[i+1&1][j])%mod+mod)%mod; } for(int j=1;j<=n;j++) sum[j]=(sum[j-1]+dp[i&1][j])%mod; } printf("%d ",dp[1&1][a]); return 0; }
旅行(travel)
Time Limit:1000ms Memory Limit:64MB
题目描述
LYK 想去一个国家旅行。这个国家共有 n 个城市,有些城市之间存在道路,我们假定这
些道路长度都是 1 的,更准确的说,共有 m 条道路。
我们定义城市 A 与城市 B 的最短路为 A 到 B 的所有路径中,经过的道路最少的那条道
路。最短路的长度为这条道路的所有道路长度之和,由于所有道路长度都为 1,因此假如 A
到 B 之间最短路的道路条数为 k,则 A 到 B 的最短路长度为 k。
我们定义整个国家的最短路为任意两个城市( A,B 与 B,A 算作不同的点对)之间的最短
路长度的和。
然而这个国家正处于危乱之中,极有可能一条道路会被恐怖分子炸毁。
LYK 想知道,万一某条道路被炸毁了,整个国家的最短路为多少。若炸毁这条道路后整
个国家不连通了,那么就输出“ INF” (不加引号)。
输入格式(travel.in)
第一行两个数 n,m。
接下来 m 行,每行两个数 u,v,表示存在一条道路连接 u,v(数据保证不存在自环)。
输出格式(travel.out)
输出 m 行,第 i 行的值表示当第 i 条道路被炸毁时,整个国家的最短路是多少,若图不
连通,则输出“ INF”。
输入样例
2 2
1 2
1 2
输出样例
2 2
数据范围
对于 20%的数据 n<=10,n<m<=100。
对于 40%的数据 1<=n<m<=100。
对于 70%的数据 1<=n<=100,n<m<=3000。
对于再另外 10%的数据对于所有节点( i 1<=i<n),存在一条边连接 i与 i+1,且 n=m,n<=100。
对于再再另外 10%的数据对于所有节点 i( 1<=i<n),存在一条边连接 i 与 i+1,且 n=m,
n<=1000。
对于再再再另外 10%的数据对于所有节点( i 1<=i<n),存在一条边连接 i 与 i+1,且 n=m,
n<=100000。
暂无正解
前70分部分(后面30容斥原理不打了):
/* 70分(后面容斥原理不会) 70分最短路树+30分容斥原理 对于前70分 首先求一遍最短路树 对于删除一条边时 如果这条边在一个点的最短路树上 则重新求一边这个点到其他点得最短路 不然则删除这条边对最短路没影响直接加上刚开始求得最短路的和 时间复杂度会从O(nm^2)-->O(n^2m) */ #include<iostream> #include<cstdio> #include<cstring> #include<queue> #define LL long long #define inf 1000000000000 #define maxx 1010 #define maxn 30010 using namespace std; int n,m,topt; int first[maxx]; int pre[maxx][maxx],f[maxx]; LL dis[maxx],sum[maxx]; struct edge { int from; int to; int next; }e[maxn*2]; queue<int>q; int init() { int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f; } void add(int x,int y) { topt++; e[topt].from=x; e[topt].to=y; e[topt].next=first[x]; first[x]=topt; } void bfs(int st) { for(int i=1;i<=n;i++) dis[i]=inf,f[i]=0; q.push(st); dis[st]=0; f[st]=1; while(!q.empty()) { int now=q.front(); q.pop(); for(int i=first[now];i;i=e[i].next) { int to=e[i].to; if(!f[to]) { dis[to]=dis[now]+1; pre[st][to]=now; f[to]=1; q.push(to); } } } for(int i=1;i<=n;i++) sum[st]+=dis[i]; } void spfa(int st,int x) { for(int i=1;i<=n;i++) dis[i]=inf,f[i]=0; q.push(st); dis[st]=0; f[st]=1; while(!q.empty()) { int now=q.front(); q.pop(); for(int i=first[now];i;i=e[i].next) { if(i==x||i==x+1)continue; LL to=e[i].to; if(!f[to]) { dis[to]=dis[now]+1; f[to]=1; q.push(to); } } } } int main() { freopen("travel.in","r",stdin); freopen("travel.out","w",stdout); n=init();m=init(); for(int i=1;i<=m;i++) { int x,y; x=init();y=init(); add(x,y);add(y,x); } for(int i=1;i<=n;i++) bfs(i); for(int i=1;i<=m*2;i+=2) { int x=e[i].from; int y=e[i].to; LL tot=0; for(int j=1;j<=n;j++) { if(pre[j][x]==y||pre[j][y]==x) { spfa(j,i); for(int k=1;k<=n;k++) tot+=dis[k]; } else tot+=sum[j]; } if(tot<inf) printf("%lld ",tot); else printf("INF "); } return 0; }