/*
B:Partition
AC 31MS 304K 729 B C++
题意:给你一个n,n可以被分解为一个或多个正整数的和,一共会有2^(n-1)种分解方法,再给你一个k,问在这么多分解方法里,出现k的次数。
分析:列一下4,5的分解方法,发现5的分解方法中2出现的次数等于4中1出现的次数,推证n中k出现的次数等于n-k+1中1出现的次数,那么有价值的数字就是n-k+1;
就只需要求出n,1的输出即可,找规律啦~找到递推公式(n>2时)An=2*A(n-1)+2^(n-2);然后两边同时除以2^n,得到通项公式(n>2时)An = 2^(n-2)*(n+3);
n<=2时打表即可。求2的幂次时用快速幂即可。
另:题解上这样写道:
我们可以特判出 n <= k的情况。
对于 1 <= k < n,我们可以等效为 n个点 排成一列,并取出其中的连续k个点 。
下面分两种情况考虑:
第一种情况,被选出的不包含端点那么有(n–k−1)种情况完成上述操作,剩下未被圈的点之间还有(n–k−2)个位置,可以在每个位置断开,所以共2^(n−k−2) ∗ (n−k−1)种方法。
第二种情况,即被选出的包含端点,那么有2种情况,并且剩余共(n–k−1)个位置,所以共2 ∗ 2^(n–k−1)种方法。
总计 2 ∗ 2^( n – k − 1) + 2^( n – k − 2) ∗ (n – k − 1) = (n – k + 3) * 2^( n – k − 2)。
题解的思想才是真快,不知何时思维才能够如此敏捷o(╯□╰)o
注意:分情况的时候,先处理一下k>n的情况,输出0;
*/
#include<stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
#define inf 1000000007
#define ll __int64
int a[4];
void init()
{
a[0] = 0;a[1] = 1;a[2] = 2;a[3] = 5;
}
int main()
{
init();
ll m,n;
int t;cin>>t;
ll maxe;
while(t--){
scanf("%I64d%I64d",&m,&n);
maxe = m-n+1;
if(maxe<=3){
printf("%d\n",a[maxe]);
continue;
}else{
maxe -= 3;
ll tmp = 1,x=2 ;
while(maxe>0){//快速幂
if(maxe%2 == 1)
{
tmp = tmp*x;
tmp%=inf;
}
maxe/=2;
x *=x;
x%=inf;
}
tmp = tmp*(m-n+3)%inf;//通项公式
printf("%I64d\n",tmp);
}
}
return 0;
}
/*
E:Deque
AC 328MS 2680K 1265B C++
题意:按顺序给你一列数,一个一个的,你可以依次将它插到一个deque里,方法是可以place到头或尾,也可以先pop掉头或尾的元素,再进行place操作,最后使deque(不下降)的长度最长
分析:对每一个数进行枚举,分别记录按顺序place后,包含该数的最长不下降和最长不上升子序列的长度len1,len2,另外也需要记录该数的重复次数same。结果就是max(len1+len2-same);
*/
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
#define M 100000+5
int dp1[M],dp2[M];
int same[M];
int n,o[M];
void up()
{
vector<int >v;//story the lenth of the current longest subsquences
int a,b;
for (int i = 0; i < n; i++)
{
int now = o[i];
a = lower_bound(v.begin(),v.end(),now)-v.begin();//找到大于等于now的第一个位置的下标
b = upper_bound(v.begin(),v.end(),now)-v.begin();//找到比now大的第一个位置的下标
//不断更新v,取最优情况。
if(b == v.size()){
v.push_back(now);
}else{
v[b] = now;
}
dp1[i] = b+1;//记录包含该数的最长不下降子序列的长度,等于b+1;
same[i] = b-a+1;
}
return;
}
void down()
{
vector<int >v;//story the lenth of the current longest subsquences
int a,b;
for (int i = 0; i < n; i++)
{
int now = o[i];
a = lower_bound(v.begin(),v.end(),now)-v.begin();
b = upper_bound(v.begin(),v.end(),now)-v.begin();
if(b == v.size()){//
v.push_back(now);
}else{
v[b] = now;
}
dp2[i] = b+1;
same[i] = min(same[i],b-a+1);
}
return;
}
int main()
{
int t;
cin>>t;
while(t--){
cin>>n;
for (int i = n-1; i >= 0; i--)
scanf("%d",&o[i]);
up();
for(int i = 0;i<n;i++)
o[i] = -o[i];//求下降的数组 == 原数组取相反数求上升的数组
down();
int ans = 0;
for (int i = 0; i < n; i++)
{
ans = max(ans,dp1[i]+dp2[i]-same[i]);
}
cout<<ans<<endl;
}
return 0;
}
顺便说一下上题里用到的lower_bound和upper_bound函数:
函数upper_bound()返回的在前闭后开区间查找的关键字的上界,如一个数组number序列1,2,2,4.upper_bound(2)后,返回的位置是3(下标)也就是4所在的位置,同样,如果插入元素大于数组中全部元素,返回的是last。(注意:此时数组下标越界!!)
返回查找元素的最后一个可安插位置,也就是“元素值>查找值”的第一个元素的位置。
函数lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置
举例如下:
一个数组number序列为:4,10,11,30,69,70,96,100.设要插入数字3,9,111.pos为要插入的位置的下标
则
pos = lower_bound( number, number + 8, 3) - number,pos = 0.即number数组的下标为0的位置。
pos = lower_bound( number, number + 8, 9) - number, pos = 1,即number数组的下标为1的位置(即10所在的位置)。
pos = lower_bound( number, number + 8, 111) - number, pos = 8,即number数组的下标为8的位置(但下标上限为7,所以返回最后一个元素的下一个元素)。
所以,要记住:函数lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置,且last的位置是越界的!!~
返回查找元素的第一个可安插位置,也就是“元素值>=查找值”的第一个元素的位置。
/*
I:I-Number
AC 312MS 400K 955 B C++
题意:给你一个正整数x,要求你输出一个数y,要满足两个要求:y>x,y的所有数位上的数的和是10的倍数。
分析:计算出x所有数位的和mod10等于m,两种情况:
1:if m!=0,而且m加到x的个位上也不产生进位,就直接加上输出。
2:m==0或会产生进位,把个位清零,向前进位,一直进,然后得到一个个位是0的数,再计算m,然后个位上加上10-m即可。
注意:用char数组做,9999....的情况要加一位。
另:本题有个小问题,如果输入带前导零的数字,应该输出时把前导零处理掉,但hdu保留了前导零,大家注意下。
*/
#include<stdio.h>
#include<iostream>
#include<cstring>
using namespace std;
char a[100005];
int main()
{
int t;
cin>>t;
int sum,last,cha,lcha;
while(t--)
{
sum = 0;
cin>>a;
int len = strlen(a)-1;
for (int i = 0; a[i] != '\0'; i++)
{
sum += (a[i]-'0')%10;
sum %=10;
if(a[i+1] == '\0')
last = a[i]-'0';
}
cha = 10-sum;
if(cha!=10 && cha+last<10){
a[len] += cha;
cout<<a<<endl;
}else{
int j = len;
if(j == 0){
cout<<"19"<<endl;
continue;
}
a[j] = '0';
a[j-1] += 1;
--j;
while(a[j]-'0'>=10 && j>0)
{
a[j] = '0';
a[--j]+=1;
}
if(j==0 && a[0]-'0' == 10)
{
a[0] = '0';
a[len] = '9';
cout<<"1"<<a<<endl;
}else{
sum = 0;
for (int i = 0; a[i] != '\0'; i++)
{
sum += (a[i]-'0')%10;
sum%=10;
}
if(sum!=0) a[len] = 10-sum+'0';
cout<<a<<endl;
}
}
}
return 0;
}
/*
H:Park Visit
AC 640MS 4468K 1113 B
题意:给你一个有向无环图,其中每条边的权值都是1,问你要visit其中k个点,至少需要花费多少?
分析:求出树的直径d(两遍dfs),如果d<=k输出k-1,否则k-1+2*(k-d);
PS: 不知道为啥bfs超时。。。
*/
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;
#define M 110000+5
vector<int >map[M];
int n;
int d[M];
void init()
{
for (int i = 1; i <= n; i++)
map[i].clear();
}
void dfs(int x,int fa,int dp)//多一个参数fa,可以减少对d数组的初始化,可以优化好多时间。
{
d[x] = dp;
for (int i = 0; i < map[x].size(); i++)
if(map[x][i] != fa)
dfs(map[x][i],x,dp+1);
}
int main()
{
//FILE *p;
//freopen_s(&p,"1008.in","r",stdin);
//freopen_s(&p,"out.txt","w",stdout);
int t,k,D,Q;
cin>>t;
while(t--)
{
scanf("%d%d",&n,&Q);
init();
int u,v;
for (int i = 1; i < n; i++)
{
scanf("%d%d",&u,&v);
map[u].push_back(v);
map[v].push_back(u);
}
int root = 1;
dfs(root,0,0);
for (int i = 2; i <= n; i++)
if(d[i]>d[root])
root = i;
dfs(root,0,0);
for (int i = 2; i <= n; i++)
if(d[i]>d[root])
root = i;
D = d[root];
while(Q--){
scanf("%d",&k);
if(k <= D+1)printf("%d\n",k-1);
else printf("%d\n",D+(k-D-1)*2);
}
}
return 0;
}