2017 济南综合班 Day 6
循环移动
(cyclic.cpp/c/pas)
(1s/256M)
问题描述
给出一个字符串S与N个操作。每个操作用三元组(L, R, K)进行描述:操作将字符串第L个到第R个位置构成的子串循环移动K次。一次循环移动就是将字符串最后的这个字符移动到第一位,其余的字符顺次后移。
例如,对于字符串abacaba,操作(L=3, R=6, K=1)后得到的字符串即为abbacaa。
求出在N个操作后得到的字符串。
输入格式(cyclic.in)
第一行一个字符串S。
第二行一个整数N,代表操作的总数。
接下来N行每行三个数L,R,K,每行代表一个操作。
输出格式(cyclic.out)
一行一个字符串,代表N个操作后的字符串。
样例输入
abbacaa
2
3 6 1
1 4 2
样例输出
ababaca
数据范围与约束
设|S|为字符串S的长度。
对于30%的数据,|S|<=100, N<=100, K<=100
对于100%的数据,|S|<=10000, N<=300, K<=1000,000,1<=L<=R<=|S|
#include<cstdio> #include<cstring> #define N 10010 using namespace std; char s[N],tmp[N]; int n,l,r,k,t,len; int main() { freopen("cyclic.in","r",stdin); freopen("cyclic.out","w",stdout); scanf("%s",s+1); len=strlen(s+1); scanf("%d",&n); while(n--) { scanf("%d%d%d",&l,&r,&k); k%=(r-l+1); if(!k) continue; t=l; for(int i=r-k+1;i<=r;i++) tmp[t++]=s[i]; for(int i=l;i<=r-k;i++) tmp[t++]=s[i]; for(int i=l;i<=r;i++) s[i]=tmp[i]; } printf("%s",s+1); }
阅读计划
(book.cpp/c/pas)
(1s/256M)
问题描述
暑假到了,Rick制定了一个长达M天的阅读计划。他一共有N本书,从1至N进行标号;Rick将它们从上至下摞成一堆。他每天都会读一本书,假设他要读编号为X的书,他会按照以下步骤:
1. 将这本书上方的所有书搬起来
2. 将这本书拿出来
3. 将搬起来的书摞回去
4. 看完后把这本书放到顶端
每本书都会有各自的重量,Rick不希望搬起太过重的书。于是他希望能重新安排这N本书的顺序,使得读完M本书之后,搬书的重量之和最小。
输入格式(book.in)
第一行两个整数N与M,分别代表书的数量和阅读的天数。
第二行N个整数,代表每本书的重量。
第三行M个整数,代表每天要读的书的编号。
输出格式(book.out)
一行一个整数,代表最小的重量之和。
样例输入
3 5
1 2 3
1 3 2 3 1
样例输出
12
数据范围与约束
对于30%的数据,N<=10.
对于100%的数据,2<=N<=500, 1<=M<=1000, 每本书重量不超过100.
00%的数据,|S|<=10000, N<=300, K<=1000,000,1<=L<=R<=|S|
贪心:按书的第一次阅读顺序摆放
#include<cstdio> #include<cstring> using namespace std; int n,m,cnt,x,ans; int weight[501],read[1001]; int first[501]; bool v[501]; int main() { freopen("book.in","r",stdin); freopen("book.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&weight[i]); for(int i=1;i<=m;i++) { scanf("%d",&x); read[i]=x; if(!first[x]) { first[x]=i; ans+=cnt; cnt+=weight[x]; } } for(int i=1;i<=m;i++) if(first[read[i]]!=i) { for(int j=i-1;j && read[j]!=read[i];j--) if(!v[read[j]]) ans+=weight[read[j]],v[read[j]]=true; memset(v,0,sizeof(v)); } printf("%d",ans); }
树集
(set.cpp/c/pas)
(1s/256M)
问题描述
给出一棵N个节点的树,每个节点上都附有一个权值ai。现在Ann想从中选出若干个节点,满足以下条件:
1. 至少选出一个节点
2. 节点之间是连通的
3. 设节点中权值最大的为ap,最小的为aq,则需要满足ap-aq不大于某个定值D。
Ann想知道有多少种选择的方式?结果对1,000,000,007取模即可。
输入格式(set.in)
第一行包含两个整数D, N,分别代表定值D与节点总数N。
第二行包含N个整数ai,分别代表每个点的权值。
接下来N-1行,每行包含两个数u, v,代表树中节点u与节点v是相连的。
输出格式(set.out)
一个整数,代表方案数模1,000,000,007的结果。
样例输入
1 4
2 1 3 2
1 2
1 3
3 4
样例输出
8
样例解释
8个选择方式为:{1}, {2}, {3}, {4}, {1, 2}, {1, 3}, {3, 4}, {1, 3, 4}。
数据范围与约束
对于30% 的数据,1<=n<=10;
对于另外的30% 的数据,d=2000.
对于100% 的数据,0<=d<=2000, 1<=n<=2000, 1<=ai<=2000.
树形DP,如何取消d的限制?
枚举x作为选的点集中权值最小的点,
那么可以选的点的点权范围:a[x]——a[x]+d
小细节:如果点权相同,那么规定只能由编号小的走到编号大的
#include<vector> #include<cstdio> #define N 2001 #define mod 1000000007 using namespace std; int d,n,l,r,ans,rt; int a[N],dp[N]; vector<int>e[N]; void dfs(int now,int last) { dp[now]=1; int siz=e[now].size(); for(int i=0;i<siz;i++) { if(e[now][i]==last) continue; if(a[e[now][i]]<l || a[e[now][i]]>r || a[e[now][i]]==l&&e[now][i]<rt) continue; dfs(e[now][i],now); dp[now]=1ll*dp[now]*(dp[e[now][i]]+1)%mod; } } int main() { freopen("set.in","r",stdin); freopen("set.out","w",stdout); scanf("%d%d",&d,&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); int u,v; for(int i=1;i<n;i++) { scanf("%d%d",&u,&v); e[u].push_back(v); e[v].push_back(u); } for(int i=1;i<=n;i++) { rt=i; l=a[i]; r=a[i]+d; dfs(i,0); ans=(ans+dp[i])%mod; } printf("%d",ans); }