前面因为没有在场上A题,赛后A后也渐渐地没写,今天来写一发,重温一下可爱的多校
Hdu 4602 Partition 1003
题意:对整数n进行无序拆分,求数k在拆分中出现了多少次
正解:把整数n一个无序拆分可以看成把n个点(每个点代表1)排成一列,在点与点之间插若干个空,数k就相当于k个连续的点。
分两种情况考虑:
1) n=k+......或者n=......+k,那么剩余的(n-k-1)位置可以插或者不插空,即2×2^(n-k-1)种方法
2) n=...+k+...,那么k可以选择(n-k-1)个位置,剩余的(n-k-2)位置可以选择插空,即(n-k-1)*2^(n-k-2)种方法
一共,(n-k+3)*2^(n-k-2)种方法。
要注意n<k的情况。
很神奇的题目,羡慕那些数学好的人!
代码:
#include <iostream>//1003 Partition
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include<cmath>
#define INF 0x7fffffff
const long long M=1e9+7;
using namespace std;
long long powm(long long a,long long b,long long c)
{
long long ans=c,tmp=a;
while(b)
{
if(b%2)
{
ans=(ans*tmp)%M;
}
tmp=(tmp*tmp)%M;
b/=2;
}
return ans;
}
int main()
{
int t;
long long n,k;
scanf("%d",&t);
while(t--)
{
cin>>n>>k;
if(n<k)printf("0\n");
else if(n==k)printf("1\n");
else
{
if(n-k-2==-1)
cout<<(n-k+3/2)<<endl;
else
{
long long ss=powm(2,n-k-2,n-k+3);
cout<<ss<<endl;
}
}
}
return 0;
}
Hdu 4604 Deque 1005
题意:按序给你一些数,每个数你可以加入双端队列(初始为空),在任何时候都你可以弹出元素。求最后能保留在双端队列中的最长不下降子序列长度。
思路:实际就是求从某个点开始,的最长不降子序列和最长不升子序列之和 减去两个中重复的数字
每拿到一个点a[i],就求在它为起点往左走的最长不上升子序列,在它的右边求最长不下降子序列,自己用手模拟时才瞬间明白
每次找分割点a[i]最好从后往前找,我试了一下,从前往后找很麻烦。。PS:从后往前求最长不下降升子序列,从前往后求最长不上升子序列
代码:
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
#include <map>
#define M 20000006
using namespace std;
int up[100005];//上升序列
int down[100005];//下降序列
int nump[100005];//记录上升序列中相同数字出现的个数
int numd[100005];
int a[100005];
int getup(int x,int y,int v)//往后找相当于下降
{
int l=x,r=y,mid;
while(l<=r)
{
mid=(l+r)>>1;
if(up[mid]>=v)l=mid+1;
else
r=mid-1;
}
return l;
}
int getdown(int x,int y,int v)
{
int l=x,r=y,mid;
while(l<=r)
{
mid=(l+r)>>1;
if(down[mid]<=v)l=mid+1;
else
r=mid-1;
}
return l;
}
int main()
{
int t,n,i,j;
scanf("%d",&t);
while(t--)
{
memset(nump,0,sizeof(nump));
memset(numd,0,sizeof(numd));
scanf("%d",&n);
for(i=1; i<=n; i++)
scanf("%d",&a[i]);
up[1]=a[n];
nump[1]=1;
down[1]=a[n];
numd[1]=1;
int s1=1,s2=1,mmax=1;
for(i=n-1; i>=1; i--)
{
int d1=getup(1,s1,a[i]);
if(d1>s1)s1++;
up[d1]=a[i];
nump[d1]=(d1>1 && up[d1-1]==a[i])? nump[d1-1]+1 : 1;
int d2=getdown(1,s2,a[i]);
if(d2>s2)s2++;
down[d2]=a[i];
numd[d2]=(d2>1 && down[d2-1]==a[i])?numd[d2-1]+1 : 1;
int t=d1+d2-min(nump[d1],numd[d2]);
if(t > mmax)
mmax=t;
}
printf("%d\n",mmax);
}
return 0;
}
Hdu 4607 Park Visit 1008
题意:一棵树,问从任意点出发,访问k个点走过的最少的边数。
思路:首先找一条树上最长链,当k大于链上的点数时,就要走一些链的节点的子树且走回链上,也就是子树的边走了两次。
代码:
#include <iostream>//1008
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include<cmath>
#define INF 0x7fffffff
#define M 1000000007
using namespace std;
vector<int>p[100005];
int vis[100005];
int dp[100005];
int t,n,m,mmax;
void Bfs(int x)
{
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
dp[i]=INF;
queue<int>q;
q.push(x);
vis[x]=1;dp[x]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
int size=p[u].size();
for(int i=0;i<size;i++)
{
int v=p[u][i];
if(dp[u]+1<dp[v])
dp[v]=dp[u]+1;
if(vis[v]==0)
{
q.push(v);
vis[v]=1;
}
}
}
}
int main()
{
scanf("%d",&t);
int u,v,k;
while(t--)
{
scanf("%d%d",&n,&m);
for(int i=0; i<=n; i++)
p[i].clear();
for(int i=1; i<=n-1; i++)
{
scanf("%d%d",&u,&v);
p[u].push_back(v);
p[v].push_back(u);
}
Bfs(1);
int posi=0,mmax=-1;
for(int i=1; i<=n; i++)
{
if(dp[i]>mmax)
{
mmax=dp[i];
posi=i;
}
}
Bfs(posi);
mmax=-1;
for(int i=1; i<=n; i++)
{
if(dp[i]>mmax)
{
mmax=dp[i];
}
}
for(int kk=0; kk<m; kk++)
{
scanf("%d",&k);
if(k<=mmax+1)
printf("%d\n",k-1);
else
printf("%d\n",mmax+(k-mmax-1)*2);
}
}
return 0;
}
Hdu 4608 I-number 1009
题意:对于给定的x,要找一个y,要求y>x,且y所有位加和为10的倍数,输出最小的y
两个数之间的差值不会超过20,所以可以一直+1知道找到要找的数,>9就向高位进一位
代码:
#include <iostream>//I-number
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include<cmath>
#define INF 0x7fffffff
const long long M=1e9+7;
using namespace std;
char a[100005];
int s[100005];
int main()
{
int t,i;
scanf("%d",&t);
getchar();
while(t--)
{
memset(s,0,sizeof(s));
scanf("%s",a);
int l=strlen(a);
int j=0;
for(i=l-1; i>=0; i--)
{
s[j++]=a[i]-'0';
}
int sum;
do
{
sum=0;
s[0]+=1;
for(i=0;i<l;i++)
{
s[i+1]+=s[i]/10;
s[i]=s[i]%10;
}
if(s[l])
l++;
for(i=0;i<l;i++)
sum+=s[i];
}while(sum%10!=0);
for(i=l-1;i>=0;i--)
printf("%d",s[i]);
printf("\n");
}
return 0;
}