20170705测试
Day1
T1重复字符串
时间限制: 1 Sec 内存限制: 256 MB题目描述
给定两个字符串a和b,我们可以定义一些操作:a*b为将字符串a和字符串b连接起来,比如a= "aoe",b= "jkw",那么a*b= "aoejkw"。进一步,我们可以有指数操作,a^0= "", a^1=a, a^2=a*a, a^n=a*(a^(n-1))=a*a*…*a (n个a)
现在给你一个字符串,你可以将它看成是a^n的形式,比如字符串"abababab",可以认为是"abab"^2, 也可以是"abababab"^1,还可以是"ab"^4。
现在问题是,给定的字符串,我们想让它变成a^n中的n达到最大,那么这个n最大是多少?例如:"abababab"最大的n是4。
输入
第一行,一个整数m,表示有m个字符串。
接下来m行每行输入一个只含小写字母的字符串。
输出
输出m行,对于每行输出相应字符串的最大n。
样例输入
样例输出
提示
30%的数据:字符串的长度≤1000;
100%的数据:字符串的长度≤1000000, m≤10,字符串内只含小写字母。
类似因数分解一下一个一个除下来,hash一下就好了。
不过正解好像是KMP(雾)
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cmath> 5 #include<cstring> 6 const int Q=1795876373; 7 #define mo(i) ((i%Q+Q)%Q) 8 #define h(i,j) (mo(f[j]-f[i-1]*fp[j-i+1])) 9 using namespace std; 10 char ch[1000005]; 11 const int w=25; 12 int nu,q[100000],fl[1000005]; 13 long long fp[1000005],f[1000005]; 14 void Build_Hash(int n){ 15 fp[0]=1; f[0]=0; 16 for (int i=1;i<=n;i++) fp[i]=mo(fp[i-1]*w); 17 for (int i=1;i<=n;i++) f[i]=ch[i]-'a'; 18 for (int i=1;i<=n;i++) f[i]=mo(f[i]+f[i-1]*w); 19 } 20 bool check(int n,int x){ 21 int s=h(1,n/x); 22 for (int i=1;i<=n;i+=n/x){ 23 if (s!=h(i,i+n/x-1)) return 0; 24 } 25 return 1; 26 } 27 int main(){ 28 for (int i=2;i<=1000000;i++){ 29 if (!fl[i]) { 30 q[++nu]=i; 31 } 32 for (int j=1;j<=nu && q[j]*i<=1000000;j++){ 33 fl[q[j]*i]=1; 34 if (i % q[j]==0) break; 35 } 36 } 37 int T; scanf("%d",&T); 38 while (T--){ 39 scanf("%s",ch); int n=strlen(ch),ans=1; 40 for (int i=n;i>=1;i--) ch[i]=ch[i-1]; 41 Build_Hash(n); 42 for (int i=1;i<=nu && q[i]<=n;i++) 43 if (n % q[i]==0){ 44 while (n % q[i]==0){ 45 if (check(n,q[i])) n/=q[i],ans*=q[i]; 46 else break; 47 } 48 } 49 printf("%d\n",ans); 50 } 51 return 0; 52 }
附上题解:
重复字符串(powerstr):
30分做法:枚举
长度范围只有1000时,我们可以枚举k, 取字符串第1个到第k个字符作为子串T,然后去验证剩下的字符串是否都是T重复得来
时间复杂度O(n^2)
100分做法:KMP,Next数组
假设字符串为S,长度为N,子串T重复K次后得到串S,那么T的长度一定为L = N/K(要整除),则T = S[1...L],将S拆分成K份,每份长度为L,则有
S[1...L] = S[L+1...2L] = S[2L+1...3L] = ... = S[(K-1)L+1...KL]
由于要保证K最大,势必L要取最小,所以根据Next函数的定义,有Next[KL] = (K-1)L;
即Next[N] = N - L,所以L = N - Next[N];
但是得出的长度L还要保证能被N整除,所以如果不能整除说明L = N,即K = 1;而如果能整除,那么K = N / (N - Next[N]);
因而我们只要对字符串S做一趟KMP,对其求Next数组,剩下的就是上述结论
时间复杂度O(n)
T2Fibonacci进制
时间限制: 1 Sec 内存限制: 256 MB题目描述
定义一种Fibonacci进制,可以将十进制数用Fibonacci数表示。Fibonacci进制中,每个位上的数值只有0或1,权值是Fibonacci数。令f0=f1=1,fi=fi-1+fi-2, N=an*fn+an-1*fn-1+…+a1*f1,写成N=anan-1..a2a1f。Fibonacci表示中,不能出现相邻的两个1。例如:自然数(十进制)表示为Fibonacci进制为1=1F,2=10F,3=100F,4=3+1=101F,5=1000F,6=5+1=1001F,7=5+2=1010F。
现在,Bsny将所有自然数按照Fibonacci进制,依次输出在屏幕上,110100101100010011010……现在,Bsny想知道这个长串的前N个数字中,包含多少个1。
输入
第一行一个整数N,表示统计范围是自然数的Fibonacci
表示的前N个数字
输出
一个数,前N个数字中1的个数。
样例输入
样例输出
提示
【样例解释】
前21个数字为110100101100010011010,共有10个1。
【数据规模】
30%的数据N≤1000;
50%的数据N≤106;
100%的数据N≤1015。
找规律,在分部求解
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #define ll long long 5 using namespace std; 6 #define N 100 7 ll n,fb[N],tol[N],tol_1[N],sum,ans,num; 8 void solve(int x,ll n){ 9 if (n==1) { 10 ans++; return; 11 } 12 ans+=n; n--; 13 int i=1; 14 while (n>=fb[i]){ 15 n-=fb[i]; ans+=tol_1[i]; i++; 16 } 17 if (n>0) solve(i,n); 18 } 19 int main(){ 20 scanf("%lld",&n); ll n_=n; 21 fb[1]=1; fb[2]=1; tol[1]=1; 22 tol_1[1]=1; tol_1[2]=1; 23 int i=1; 24 while (n>=tol[i]){ 25 n-=tol[i]; ans+=tol_1[i]; num+=fb[i]; //cout<<i<<" "<<tol_1[i]<<endl; 26 i++; 27 if (i>2) fb[i]=fb[i-1]+fb[i-2]; 28 tol[i]=fb[i]*(ll)i; 29 if (i>2) sum+=tol_1[i-2],tol_1[i]=fb[i]+sum; 30 } 31 fb[i+1]=fb[i]+fb[i-1]; 32 long long g=n/(ll)i,p=n%(ll)i; num+=g+1; 33 if (p>0){ 34 int f=1,nu=0; 35 for (int j=i+1;j>=2;j--){ 36 nu++; 37 if (nu>p) break; 38 if (f & num>=fb[j]) ans++,num-=fb[j],f=0; 39 else f=1; 40 } 41 } 42 if (g>0) solve(i,g); 43 cout<<ans<<endl; 44 return 0;
附上详细题解:
N很大,先尝试几个小数据。可以发现,每个Fibonacci表示的长度,和Fibonacci数大小有关(1,2,3,5,8,13,21……),这些值最高位上是1,后面全是0,即第一个Fibonacci表示长度为i的数是fib[i]。因此按照长度对Fibonacci表示进行分类,长度为i的数有fib[i-1]个,看做是第i组。那么第i组的总长度len[i] = fib[i-1]*i。
接下来,看1出现的次数。长度为i的数的表示中,第i-1位肯定是0。
Sum[i]表示前i组的1的个数。可以得到如下式子:Sum[i]=sum[i-1]+fib[i-1]+sum[i-2]。第i组首位1的个数为fib[i-1],i-1位为0,那么最后的i-2位的情况,恰好为1~i-2组的所有情况。
整体算法也就明了了:
1) 求出N位所在的Fibonacci表示的数的长度t
2) 求1~t中Fibonacci表示中1出现的个数。
3) 继续求解剩余字符的1。
例如求解得到最后对应Fibonacci表示为x=100100
1) 对于长度为1~5的Fibonacci表示中1的个数为sum[5],i<=100000中1的个数即为sum[5]+1。
2) 对于100000<i<=100100,最高位都是1,共有fib[3]个,后三位1的个数为sum[2]+1。
3) 1的总个数为sum[5]+1+fib[3]+sum[2]+1。
最后细节比较多,要实现的仔细一些。
T3发奖金
时间限制: 1 Sec 内存限制: 256 MB题目描述
Bsny最近公司运作不佳,本年度利润才m元,但员工的奖金还是要发的,公司有n个员工,怎么发奖金这个完全由老板Bsny自己决定。Bsny想要么把这m元全发了,激励一下员工,但具体怎么分配方案有很多。比如m=1, n=2, 那么可以员工1发1元,员工2发0元;也可以员工1发0元,员工2发1元,有两种方案。
但其实,Bsny还是有点吝啬的,他想这m元不一定全部作为奖金,可以部分留给自己,这样的话,发奖金的方案数就更多了。还是以m=1, n=2为例子:
方案1:员工1发1元,员工2发0元
方案2:员工1发0元,员工2发1元
方案3:员工1发0元,员工2发0元
意味着老板Bsny发的奖金范围为[0, m]。
好奇的Bsny想知道,给定n和m,他有多少种发奖金的方案?这个答案很大,所以再给定一个p,最终的答案取模p的余数.
输入
第一行三个整数n, m, p。
输出
仅一行,一个整数表示最终的答案取模p的余数。
样例输入
样例输出
提示
对于p:设p=p1^c1 * p2^c2 * p3^c3 * … *pt ^ ct,pi为质数。
20%的数据:1 ≤ n, m≤ 15;
40%的数据:1≤n, m≤1000,p=10007;
60%的数据:保证t=1,ci=1,pi^ci≤10^5;
80%的数据:t≤2,ci=1,pi≤10^5;
100%的数据:1≤ n, m≤10^9,1≤pi^ci≤10^5,所有P不超过2^31-1。
这道题很神奇,听说还要用一些求和式,蒟蒻不会。
100分做法:组合+质因数分解+逆元+中国剩余定理
题目相当于求n个数的和不超过m的方案数。
首先如果是恰好等于m,那么就等价于求方程x1 + x2 + ... + xn = m的解的个数,利用插板法可得到公式:C(n + m - 1, m)
现在要求不大于m的,相当于对i = 0 ... m对C(n + i - 1, i)求和,根据pascal递推式可以得到答案为C(n + m, m)
现在就需要求C(n + m, m) mod P
这里我们主要要解决如何快速计算n! mod P
以及当分母有m! mod P的情况
1. 当n,m都比较小的时候,同时P为比较大的素数时,可以直接利用逆元求解,当n,m比较大的时候,见下面两种情况(以下参考魏铭2011年国家集训队作业)
2. 当P为素数的情况:
我们发现n! mod P的计算过程是以P为周期的,举例如下:
n = 10, P = 3
n! = 1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10
= 1 * 2 *
4 * 5 *
7 * 8 *
10 *
3 * 6 * 9
= (1 * 2)3 *
33 * (1 * 2 * 3)
最后一步中的1 * 2 *3可递归处理。
因为P的倍数与P不互质,所以P的倍数不能直接乘入答案,应当用一个计数器变量cnt来保存答案中因子P的个数。
我们提前预处理出fac[i] = 1 * 2 * 3 * … * (i – 1) * i mod P,函数calcfac(n)返回n! mod P的值,power(a, b, c)返回ab mod c的值,可用快速幂在O(logb)的时间内完成。
typedef long long LL;
LL calcfac(LL n)
{
if (n <P) return fac[n];
LL seg = n / P, rem = n % P;
LL ret = power(fac[P - 1], seg, P); //fac[P - 1]重复出现了seg次
ret = ret * fac[rem] % P; //除去seg次fac[P – 1]外,剩下的零头
cnt += n / P; //提出n / P个因子P
ret = ret * calcfac(n / P) % P; //递归处理
return ret;
}
于是n! mod p的计算可在O(logn)的时间内解决。
对于分母中的n!,方法是相似的。若a为正整数,a * a’ = 1(mod P),那么我们称a’为a的逆元,记作a-1,并有b / a(mod P) = b * a-1(mod P)。这样我们只需要把预处理fac[i] = 1 * 2 * 3 * … * (i – 1) * i mod P更换为inv[i] = 1-1 * 2-1 * 3-1* … * (i – 1) -1 * i-1 mod P,其计算方法与分子中的n!计算相差无几,具体可参考我的代码。求逆元可以使用扩展欧几里得算法。
3. 当p为合数时
对于某些测试点,我们发现P分解后只有2个因子,并且不相同,所以我们可以对这两个因子分别运行算法2,最后用中国剩余定理合并即可。
对于剩下的数据,可以对P进行质因数分解,得到P = p1c1 * p2c2 * … * ptct。
对于每个1≤i≤t,以pici为模运行算法2,最后用中国剩余定理合并。这里pici不一定为质数,不过只需对原算法稍加修改即可。令P = pici,fac[i] = 除去pi的倍数外i的阶乘。例如pi = 3,ci = 2,那么fac[10] = 1 * 2 * 4 * 5 * 7 * 8 * 10,除去了3的倍数3、6和9。阶乘依然是以P为周期的,calcfac(n)与算法2主体相同,只是统计因子个数时,应使用ret += n / pi而不是ret += n / P,递归处理时也应该是calcfac(n / pi)而不是calcfac(n / P)。
时间复杂度O(t * n)
标称:
1 #include <cstdio> 2 #include <iostream> 3 using namespace std; 4 5 typedef long long LL; 6 const int N = 100005; 7 8 LL w[N]; 9 LL fac[N], inv[N]; 10 LL p[N], c[N], cntP, cnt, a0, a1, m0, m1, P, mo; 11 int n, m, ID; 12 13 LL power(LL x, LL y, LL M) 14 { 15 LL ans = 1; 16 for (; y > 0; y >>= 1) { 17 if (y & 1) { 18 ans *= x; 19 if (M != -1) ans %= M; 20 } 21 x *= x; 22 if (M != -1) x %= M; 23 } 24 return ans; 25 } 26 27 LL calcfac(LL n) 28 { 29 if (n < p[ID]) return fac[n]; 30 LL seg = n / P, rem = n % P; 31 LL ret = power(fac[P - 1], seg, P); 32 ret = ret * fac[rem] % P; 33 cnt += n / p[ID]; 34 ret = ret * calcfac(n / p[ID]) % P; 35 return ret; 36 } 37 38 LL calcinv(LL n) 39 { 40 if (n < p[ID]) return inv[n]; 41 LL seg = n / P, rem = n % P; 42 LL ret = power(inv[P - 1], seg, P); 43 ret = ret * inv[rem] % P; 44 cnt -= n / p[ID]; 45 ret = ret * calcinv(n / p[ID]) % P; 46 return ret; 47 } 48 49 LL exgcd(LL a, LL b, LL &x, LL &y) 50 { 51 if (b == 0) { 52 x = 1; y = 0; 53 return a; 54 } else { 55 LL r = exgcd(b, a % b, y, x); 56 y -= a / b * x; 57 return r; 58 } 59 } 60 61 LL getinv(LL i, LL P) 62 { 63 LL x, y; 64 exgcd(i, P, x, y); 65 x %= P; 66 x += (x < 0) * P; 67 return x; 68 } 69 70 LL solve() 71 { 72 P = power(p[ID], c[ID], -1); 73 fac[0] = 1; 74 for (int i = 1; i < P; ++i) { 75 fac[i] = fac[i - 1]; 76 if (i % p[ID] != 0) 77 fac[i] = fac[i] * i % P; 78 } 79 inv[0] = 1; 80 for (int i = 1; i < P; ++i) { 81 inv[i] = inv[i - 1]; 82 if (i % p[ID] != 0) 83 inv[i] = inv[i] * getinv(i, P) % P; 84 } 85 cnt = 0; 86 LL ret = calcfac(n); 87 //cout << ret << " " << cnt << endl; 88 for (int i = 1; i <= m; ++i) { 89 ret = ret * calcinv(w[i]) % P; 90 //cout << w[i] << " " << ret << " " << cnt << endl; 91 } 92 ret = ret * power(p[ID], cnt, P) % P; 93 return ret; 94 } 95 96 void solve_equation(LL a, LL b, LL c, LL &k, LL &l) 97 { 98 exgcd(a, b, k, l); 99 k *= c; 100 k %= b; 101 k += (k < 0) * b; 102 return; 103 } 104 105 int main() 106 { 107 freopen("reword.in", "r", stdin); 108 freopen("reword.out", "w", stdout); 109 cin>>n>>m>>mo; 110 for (int i = 2; i < 100000; ++i) if (mo % i == 0) { 111 p[++cntP] = i; 112 for (; mo % i == 0; mo /= i) ++c[cntP]; 113 } 114 w[1]=n,w[2]=m; 115 n=n+m; 116 m=2; 117 LL sw = 0; 118 for (int i = 1; i <= m; ++i) sw += w[i]; 119 if (n - sw > 1) w[++m] = n - sw; 120 for (int i = 1; i <= cntP; ++i) { 121 ID = i; 122 a1 = solve(); 123 m1 = power(p[i], c[i], -1); 124 if (i == 1) { 125 a0 = a1; 126 m0 = m1; 127 } else { 128 LL k, l; 129 solve_equation(m0, m1, a1 - a0, k, l); 130 a0 = a0 + k * m0; 131 m0 *= m1; 132 } 133 } 134 printf("%d\n", a0); 135 return 0; 136 }
Day2
T1单词接龙1
时间限制: 5 Sec 内存限制: 256 MB Special Judge题目描述
Bsny从字典挑出N个单词,并设计了接龙游戏,只要一个单词的最后两个字母和另一个单词的前两个字母相同,那么这两个单词就可以有序的连接起来。
Bsny想要知道在所给的所有单词中能否按照上述方式接龙组成一个单词环(可能是多个),若能,求所有环的环中单词平均长度最大值。
输入
第一行一个整数N,表示单词数量。
接下来N行,每行一个字符串,仅包含小写字母。
输出
若能组成单词环,输出环中单词的最大平均长度,结果保留2位小数;否则输出"No solution."(不包括双引号)。精度误差在0.01都算正确。
样例输入
样例输出
提示
20%的数据:n≤20;
70%的数据:n≤1000;
100%的数据:n≤100000,每个单词长度不超过1000。输入数据比较大,C/C++的同学用scanf输入。
哇,这道题做的吐血,一开始想到个单词连线,然后变成26^2状态连边,然后再二分上卡住了,二分+SPFA。
详细点:
单词看成边,两个字母组合看成点。这样,庞大的输入数据就变成了一个点数至多为262的有向图。
接下来就是在有向图上找平均边长最大的环。可以用二分答案。二分一个len,将所有边权都减去len,若图中存在正权环,那么len可以增大,否则就减小。直到找到答案。
判断是否存在环,用SPFA,检查某个点入队次数,超过总点数即存在环。
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstring> 5 #include<cmath> 6 #include<cstdlib> 7 #include<vector> 8 using namespace std; 9 typedef long long ll; 10 typedef long double ld; 11 typedef pair<int,int> pr; 12 const double pi=acos(-1); 13 #define rep(i,a,n) for(int i=a;i<=n;i++) 14 #define per(i,n,a) for(int i=n;i>=a;i--) 15 #define Rep(i,u) for(int i=head[u];i;i=Next[i]) 16 #define clr(a) memset(a,0,sizeof(a)) 17 #define pb push_back 18 #define mp make_pair 19 #define fi first 20 #define sc second 21 #define pq priority_queue 22 #define pqb priority_queue <int, vector<int>, less<int> > 23 #define pqs priority_queue <int, vector<int>, greater<int> > 24 #define vec vector 25 ld eps=1e-9; 26 ll pp=1000000007; 27 ll mo(ll a,ll pp){if(a>=0 && a<pp)return a;a%=pp;if(a<0)a+=pp;return a;} 28 ll powmod(ll a,ll b,ll pp){ll ans=1;for(;b;b>>=1,a=mo(a*a,pp))if(b&1)ans=mo(ans*a,pp);return ans;} 29 void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); } 30 //void add(int x,int y,int z){ v[++e]=y; next[e]=head[x]; head[x]=e; cost[e]=z; } 31 int dx[5]={0,-1,1,0,0},dy[5]={0,0,0,-1,1}; 32 ll read(){ ll ans=0; char last=' ',ch=getchar(); 33 while(ch<'0' || ch>'9')last=ch,ch=getchar(); 34 while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar(); 35 if(last=='-')ans=-ans; return ans; 36 } 37 #include<queue> 38 queue<int> Q; 39 #define N 677 40 #define N_ 100005 41 const int inf=100000005; 42 int n,dis[N],vis[N],ti[N],head[N_],v[N_],c[N_],nex[N_],e; 43 void add(int u,int v_,int c_){ 44 v[++e]=v_; nex[e]=head[u]; head[u]=e; c[e]=c_; 45 } 46 bool spfa(int s,int x){ 47 for (int i=1;i<N;i++) dis[i]=-inf,vis[i]=0,ti[i]=0; 48 dis[s]=0; vis[s]=1; ti[s]=1; Q.push(s); 49 while (!Q.empty()){ 50 int u=Q.front(); Q.pop(); 51 //cout<<u<<endl; 52 for (int i=head[u];i;i=nex[i]){ 53 int v_=v[i]; 54 if (dis[u]+c[i]*100-x>dis[v_]){ 55 dis[v_]=dis[u]+c[i]*100-x; //cout<<v_<<" "<<dis[v_]<<endl; 56 if (!vis[v_]){ 57 ti[v_]++; 58 if (ti[v_]>N) return 1; 59 Q.push(v_); vis[v_]=1; 60 } 61 } 62 } 63 vis[u]=0; 64 } 65 return 0; 66 } 67 int main(){ 68 n=read(); 69 for (int i=1;i<=n;i++){ 70 char ch[1005]; scanf("%s",ch); int l=strlen(ch),h,t; 71 h=(ch[0]-'a')*26+(ch[1]-'a'); 72 t=(ch[l-2]-'a')*26+(ch[l-1]-'a'); 73 //cout<<h<<" "<<t<<" "<<l<<endl; 74 add(h,t,l); 75 add(N-1,h,0); 76 } 77 int l=0,r=100000; 78 while (l<r){ 79 int mid=(l+r+1)>>1; 80 //cout<<l<<" "<<r<<endl; 81 //cout<<spfa(0,mid); 82 if (spfa(N-1,mid)) l=mid; 83 else r=mid-1; 84 } 85 printf("%.2f",1.0*l/100); 86 return 0; 87 }
T2帮助Bsny
时间限制: 1 Sec 内存限制: 256 MB题目描述
Bsny的书架乱成一团了,帮他一下吧!
他的书架上一共有n本书,我们定义混乱值是连续相同高度书本的段数。例如,如果书的高度是30,30,31,31,32,那么混乱值为3;30,32,32,31的混乱值也为3。但是31,32,31,32,31的混乱值为5,这实在是太乱了。
Bsny想尽可能减少混乱值,但他有点累了,所以他决定最多取出k本书,再随意将它们放回到书架上。你能帮助他吗?
输入
第一行两个整数n,k,分别表示书的数目和可以取出的书本数目。
接下来一行n个整数表示每本书的高度。
输出
仅一行一个整数,表示能够得到的最小混乱值。
样例输入
样例输出
提示
20%的数据:1≤n≤20,k=1。
40%的数据:书的高度不是25就是32,高度种类最多2种。
100%的数据:1≤k≤n≤100,注意所有书本高度在[25,32]。
略解:
N高度差很小,可以对这个宽度只有8的高度差状态压缩进行DP。每本书抽出来后,要么放在和它等高的书旁边,要不放在一边作为一个新的高度。
状态为:f[i][j][k][mask],表示前i本书已经抽出了j本,前i本中没被抽出的书里最后一本书的高度是k,mask是一个0~2^8-1的二进制,表示前i本中没被抽出的书里高度的存在情况。
整体表示前i本书中没被抽出的书组成的序列的最小混乱度。
然后枚举第i本书是否被抽出。
详解:
http://blog.csdn.net/lhq_er/article/details/74905871
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstring> 5 #include<cmath> 6 #include<cstdlib> 7 #include<vector> 8 using namespace std; 9 typedef long long ll; 10 typedef long double ld; 11 typedef pair<int,int> pr; 12 const double pi=acos(-1); 13 #define rep(i,a,n) for(int i=a;i<=n;i++) 14 #define per(i,n,a) for(int i=n;i>=a;i--) 15 #define Rep(i,u) for(int i=head[u];i;i=Next[i]) 16 #define clr(a) memset(a,0,sizeof(a)) 17 #define pb push_back 18 #define mp make_pair 19 #define fi first 20 #define sc second 21 #define pq priority_queue 22 #define pqb priority_queue <int, vector<int>, less<int> > 23 #define pqs priority_queue <int, vector<int>, greater<int> > 24 #define vec vector 25 ld eps=1e-9; 26 ll pp=1000000007; 27 ll mo(ll a,ll pp){if(a>=0 && a<pp)return a;a%=pp;if(a<0)a+=pp;return a;} 28 ll powmod(ll a,ll b,ll pp){ll ans=1;for(;b;b>>=1,a=mo(a*a,pp))if(b&1)ans=mo(ans*a,pp);return ans;} 29 void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); } 30 //void add(int x,int y,int z){ v[++e]=y; next[e]=head[x]; head[x]=e; cost[e]=z; } 31 int dx[5]={0,-1,1,0,0},dy[5]={0,0,0,-1,1}; 32 ll read(){ ll ans=0; char last=' ',ch=getchar(); 33 while(ch<'0' || ch>'9')last=ch,ch=getchar(); 34 while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar(); 35 if(last=='-')ans=-ans; return ans; 36 } 37 const int inf=1e08; 38 int dp[102][102][9][257],a[102]; 39 int ad(int a,int b){ 40 int add=0; 41 for (int i=0;i<8;i++){ 42 if ((b&(1<<i)) && !(a&(1<<i))) add++; 43 } 44 return add; 45 } 46 int main() 47 { 48 int n=read(),K=read(),sta_=0,ans; 49 for (int i=1;i<=n;i++) a[i]=read(),a[i]-=25,sta_|=(1<<a[i]); 50 for (int i=0;i<=n;i++) 51 for (int j=0;j<=K;j++) 52 for (int k=0;k<9;k++) 53 for (int sta=0;sta<(1<<8);sta++) dp[i][j][k][sta]=inf; 54 dp[0][0][8][0]=0;ans=inf; 55 for (int i=0;i<n;i++) 56 for (int j=0;j<=K;j++) 57 for (int k=0;k<9;k++) 58 for (int sta=0;sta<(1<<8);sta++){ 59 dp[i+1][j][a[i+1]][sta|(1<<a[i+1])]=min(dp[i+1][j][a[i+1]][sta|(1<<a[i+1])],(k==a[i+1]?dp[i][j][k][sta]:dp[i][j][k][sta]+1)); 60 dp[i+1][j+1][k][sta]=min(dp[i+1][j+1][k][sta],dp[i][j][k][sta]); 61 } 62 for (int i=0;i<9;i++) 63 for (int sta=0;sta<(1<<8);sta++) 64 ans=min(ans,dp[n][K][i][sta]+ad(sta,sta_)); 65 cout<<ans<<endl; 66 return 0; 67 }
T3分组
时间限制: 1 Sec 内存限制: 256 MB题目描述
Bsny所在的精灵社区有n个居民,每个居民有一定的地位和年龄,ri表示第i个人的地位,ai表示第i个人的年龄。
最近社区里要举行活动,要求几个人分成一个小组,小组中必须要有一个队长,要成为队长有这样的条件:
1、队长在小组中的地位应该是最高的(可以并列第一);
2、小组中其他成员的年龄和队长的年龄差距不能超过K。
有些人想和自己亲密的人组在同一个小组,同时希望所在的小组人越多越好。比如x和y想在同一个小组,同时希望它们所在的小组人越多越好,当然,它们也必须选一个符合上述要求的队长,那么问你,要同时包含x和y的小组,最多可以组多少人?
输入
第一行两个整数n和K;
接下来一行输入n个整数:r1, r2, …, rn
接下来一行输入n个整数:a1, a2, …, an
接下来输入Q表示有Q个询问;
接下来Q行每行输入x, y,表示询问:当x和y组在同一个小组,它们小组最多可以有多少人(x和y也有可能被选为队长,只要它们符合条件)。
输出
对于每个询问,输出相应的答案,每个答案占一行。
当x和y无法在同一组时,输出-1(比如x的年龄是1, y的年龄是100,K=1,无论谁当队长,x和y两者中,总会有人跟队长的年龄差距超过K,那么输出-1)。
样例输入
样例输出
提示
【样例解释】
询问1:当第5个人和第3个人想在一组时,小组成员可以有{1, 3, 4, 5},选择3当队长,而2不可以加入,因为2加入的话,5和2的年龄差距为2,超过K=1了;
询问2:当第2个人和第3个人想在一组时,可以选择{1, 2, 3};
询问3:当2和5想在一起时,无法满足要求;
询问4:当4和1想在一起时,可以选择{1, 3, 4, 5};
【数据规模】
20%的数据:2≤n≤100,0≤ k≤100,1≤ ri, ai ≤100,1≤ q≤ 100;
40%的数据:2≤ n≤1000,0≤ k≤ 1000,1≤ ri, ai ≤ 1000,1≤ q≤ 1000;
60%的数据:2≤ n≤ 104,0≤ k≤ 109,1≤ ri, ai ≤ 109, 1≤ q≤ 104;
100%的数据:2≤ n≤ 105,0≤ k≤ 109,1≤ ri, ai ≤ 109,1≤ q≤ 105,1≤ x, y≤ n, x≠y。
数据结构题。还有做这题的时候一开始用了cout一直TLE,以为是数据结构写炸了,所以不要用cout!!!
首先对于每个人,我们可以预处理如果他是队长的话,最多可以有少人组队:
这里需要对r进行从小到大排序,排序完后,我们可以从小到大遍历每个人,利用树状数组统计[ai-k, ai+k]的人数,即为第i个人作为队长最大组队人数
这里要对相同r的时候特殊处理一下
然后,对于一组询问x,y, 我们可以计算出能包含x,y的组,队长的a和r的限制条件,即a的范围为[max(x.a-k, y.a+k), min(x.a+k, y.a+k)], r的范围为r>=max(x.r, y.r)
我们可以在符合a,r范围的所有人中寻找最大值
但考虑到询问比较大,我们可以采取离线的方式处理询问:
用rmin表示满足询问r的最小值,即max(x.r, y.r),对询问根据rmin从大到小排序,接下来的处理就是求[max(x.a-k, y.a+k), min(x.a+k, y.a+k)]范围中最大值是多少,这个可以用线段树来维护和求解
如果max(x.a-k, y.a+k)> min(x.a+k, y.a+k)或者找不到符合r的人,那么答案为-1
因此,总的复杂度为O(nlogn)
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #include<cstring> 5 #include<cmath> 6 #include<cstdlib> 7 #include<vector> 8 using namespace std; 9 typedef long long ll; 10 typedef long double ld; 11 typedef pair<int,int> pr; 12 const double pi=acos(-1); 13 #define rep(i,a,n) for(int i=a;i<=n;i++) 14 #define per(i,n,a) for(int i=n;i>=a;i--) 15 #define Rep(i,u) for(int i=head[u];i;i=Next[i]) 16 #define clr(a) memset(a,0,sizeof(a)) 17 #define pb push_back 18 #define mp make_pair 19 #define fi first 20 #define sc second 21 #define pq priority_queue 22 #define pqb priority_queue <int, vector<int>, less<int> > 23 #define pqs priority_queue <int, vector<int>, greater<int> > 24 #define vec vector 25 ld eps=1e-9; 26 ll pp=1000000007; 27 ll mo(ll a,ll pp){if(a>=0 && a<pp)return a;a%=pp;if(a<0)a+=pp;return a;} 28 ll powmod(ll a,ll b,ll pp){ll ans=1;for(;b;b>>=1,a=mo(a*a,pp))if(b&1)ans=mo(ans*a,pp);return ans;} 29 void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); } 30 //void add(int x,int y,int z){ v[++e]=y; next[e]=head[x]; head[x]=e; cost[e]=z; } 31 int dx[5]={0,-1,1,0,0},dy[5]={0,0,0,-1,1}; 32 ll read(){ ll ans=0; char last=' ',ch=getchar(); 33 while(ch<'0' || ch>'9')last=ch,ch=getchar(); 34 while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar(); 35 if(last=='-')ans=-ans; return ans; 36 } 37 #define N 100005 38 #define N_ N+N+N 39 struct node{int r,a;}f[N]; 40 struct node_{int x,y,r,i;}f_[N]; 41 struct ans_{int c,r,a;}ans[N]; 42 bool cmp(node a,node b){return a.r<b.r;} 43 bool cmp_(node_ a,node_ b){return a.r>b.r;} 44 int T[N_+N_+N_+N_]; 45 int T_[N_],r[N],a[N],l[N_],Anss[N],nu=0; 46 #define l_(i) (lower_bound(l+1,l+1+nu,i)-l) 47 #define r_(x) (upper_bound(l+1,l+nu+1,x)-l-1) 48 void add(int x){ 49 for (int i=x;i<=nu;i+=(i&(-i))) T_[i]++; 50 } 51 int sum(int x){ 52 int Sum=0; 53 for (int i=x;i>0;i-=(i&(-i))) Sum+=T_[i]; 54 return Sum; 55 } 56 void update(int x){ 57 T[x]=max(T[x+x],T[x+x+1]); 58 } 59 void change(int l,int r,int w,int x,int c){ 60 if (l>r) return; 61 if (l==r && r==x) { 62 T[w]=max(T[w],c); return ; 63 } 64 int mid=(l+r)>>1; 65 if (x<=mid) change(l,mid,w+w,x,c); 66 else change(mid+1,r,w+w+1,x,c); 67 update(w); 68 } 69 int find(int l,int r,int w,int x,int y){ 70 if (x>y) return -1; 71 if (l>r) return -1; 72 if (x<=l && y>=r) return T[w]; 73 int mid=(l+r)>>1; 74 if (y<=mid){ 75 return find(l,mid,w+w,x,y); 76 } else { 77 if (x>mid) return find(mid+1,r,w+w+1,x,y); 78 else return max(find(l,mid,w+w,x,y),find(mid+1,r,w+w+1,x,y)); 79 } 80 } 81 int main() 82 { 83 //freopen("rand.in","r",stdin); 84 //freopen("my.out","w",stdout); 85 int n=read(),K=read(),ansn=0; 86 for (int i=1;i<=n;i++) 87 f[i].r=read(),r[i]=f[i].r; 88 for (int i=1;i<=n;i++) 89 f[i].a=read(),a[i]=f[i].a,l[++nu]=a[i]; 90 int q=read(); 91 for (int i=1;i<=q;i++) 92 f_[i].x=read(),f_[i].y=read(),f_[i].r=max(r[f_[i].x],r[f_[i].y]),f_[i].i=i; 93 sort(f+1,f+n+1,cmp); 94 sort(l+1,l+nu+1); 95 for (int i=1;i<=n;) { 96 int j=i+1; add(l_(f[i].a)); 97 while (f[i].r==f[j].r) 98 add(l_(f[j].a)),j++; 99 for (int k=i;k<j;k++) 100 ans[++ansn].c=sum(r_(f[k].a+K))-sum(l_(f[k].a-K)-1),ans[ansn].r=f[k].r,ans[ansn].a=f[k].a; 101 i=j; 102 } 103 memset(T,255,sizeof(T)); 104 sort(f_+1,f_+q+1,cmp_); 105 int e=ansn; 106 for (int i=1;i<=q;i++){ 107 while (e>=1 && ans[e].r>=f_[i].r) 108 change(1,nu,1,l_(ans[e].a),ans[e].c),e--; 109 int Min=max(l_(a[f_[i].x]-K),l_(a[f_[i].y]-K)),Max=min(r_(a[f_[i].x]+K),r_(a[f_[i].y]+K)); 110 Anss[f_[i].i]=find(1,nu,1,Min,Max); ; 111 } 112 for (int i=1;i<=q;i++) printf("%d\n",Anss[i]); 113 return 0; 114 }