传送门:http://acm.hdu.edu.cn/showproblem.php?pid=5878
题目大意:有一些数可以写成 2a3b5c7d 的形式,称之为”I count two three numbers”.输入一个数,问比他大的最小的”I count two three numbers”是什么?
题目分析:打表预处理,发现1e9范围内的那种数只有约6000个,都列出来,然后排个序,lower_bound二分查找之。
在这里顺便总结下吧,lower_bound(start,end,n)
和upper_bound(start,end,n)
,还有binary_search(start,end,n)
的区别如下:
2016.11.16 cmershen 修正:
1. lower_bound算法返回一个非递减序列[first, last)中的第一个大于等于值val的位置。
2. upper_bound算法返回一个非递减序列[first, last)中第一个大于val的位置。
3. binary_search返回true/false。
by the way,第三个函数应该很少会有人用吧~~
#include
using namespace std;
typedef long long ll;
ll num[6000];
int t,n;
int pre() {
int i=0;
ll cur=1;
//2^30,3^19,5^13,7^11 > 1e9
for(int a=0;a<=30;a++) {
for(int b=0;b<=19;b++) {
for(int c=0;c<=13;c++) {
for(int d=0;d<=11;d++) {
cur=pow(2,a)*pow(3,b);
if(cur>1e9) break;
cur*=pow(5,c);
if(cur>1e9) break;
cur*=pow(7,d);
if(cur>1e9) break;
num[i++]=cur;
}
}
}
}
sort(num,num+i);
return i;
}
int main() {
int i=pre();
scanf("%d",&t);
while(t--) {
scanf("%d",&n);
int p=lower_bound(num,num+i,n)-num;
printf("%d\n", num[p]);
}
}
传送门:http://acm.hdu.edu.cn/showproblem.php?pid=5879
题目大意:输入n,求
∑k=1n1k2 ,保留五位小数。
题目分析:我们在高数中学过,(啥时候学的我怎么不记得了,捂脸)那个和收敛于
π26≈1.64493 。(证明略,本糖也不知道~)那么就先打表枚举下,发现n取到110293的时候,前五位小数就是1.64493了,所以如果输入的n大于110293的时候,就直接输出1.64493,否则查表。
这里有个坑,就是题目中没说n多大, 所以是任意大的,要用字符串读入。
#include
using namespace std;
typedef long long ll;
double f[110300];
char s[1111];
void pre() {
f[0]=0.0;
f[1]=1.0;
ll i=2;
while(i<110300) {
f[i]=f[i-1]+1.0/(i*i);
i++;
}
}
int main() {
pre();
while(scanf("%s",s)!=EOF) {
if(strlen(s)>=7) {
printf("1.64493\n");
}
else {
int a;
sscanf(s,"%d",&a);
if(a>=110293)
printf("1.64493\n");
else
printf("%.5lf\n", f[a]);
}
}
}
http://acm.hdu.edu.cn/showproblem.php?pid=5880
题目大意:
给出一些“和谐单词”,和一段文章,将和谐单词用等长星号代替,忽略大小写。
题目分析:
一眼就看出来是ac自动机,模板题。下面的代码可以当做模板用,若注释第70行则不忽略大小写。
不熟悉ac自动机的同学请看这里的讲解:http://hihocoder.com/problemset/problem/1036
/*
*@author:Dan__ge (modified by cmershen)
*@description:AC自动机模板,输入和谐字典,和待匹配的字符串,把字符串都和谐成星号,去掉70行的注释则变成大小写不敏感!!!
*@source:hdu 5880
*/
#include
#include
#include
#include
#include
using namespace std;
const int maxn=1000010;
int ans[maxn],cnt[maxn];
struct Trie{
int next[maxn][26],fail[maxn],end[maxn];
int root,L,ko;
int newnode(){
for(int i = 0;i < 26;i++)
next[L][i] = -1;
end[L++] = 0;
return L-1;
}
void init(){
L = 0,ko=0;
root = newnode();
memset(ans,0,sizeof(ans));
}
void insert(char buf[]){
int len = strlen(buf);
int now = root;
for(int i = 0;i < len;i++){
if(next[now][buf[i]-'a'] == -1)
next[now][buf[i]-'a'] = newnode();
now = next[now][buf[i]-'a'];
}
if(end[now]==0){
ko++;end[now]=ko;
cnt[ko]=len;
}else{
if(cnt[end[now]]void build(){
queue<int>Q;
fail[root] = root;
for(int i = 0;i < 26;i++)
if(next[root][i] == -1) next[root][i] = root;
else{
fail[next[root][i]] = root;
Q.push(next[root][i]);
}
while( !Q.empty() ){
int now = Q.front();
Q.pop();
for(int i = 0;i < 26;i++)
if(next[now][i] == -1) next[now][i] = next[fail[now]][i];
else{
fail[next[now][i]]=next[fail[now]][i];
Q.push(next[now][i]);
}
}
}
void query(char buf[]){
int len = strlen(buf);
int now = root;
int res = 0;
for(int i = 0;i < len;i++){
if(buf[i]>='a'&&buf[i]<='z') now = next[now][buf[i]-'a'];
else if(buf[i]>='A'&&buf[i]<='Z') now = next[now][buf[i]-'A'];
else continue;
int temp = now;
while( temp != root ){
ans[i+1]--;ans[i-cnt[end[temp]]+1]++;
temp = fail[temp];
}
}
}
};
char buf[maxn];
Trie ac;
int main(){
int T,n;
scanf("%d",&T);
while( T-- ){
scanf("%d",&n);
ac.init();
for(int i = 0;i < n;i++){
scanf("%s",buf);
ac.insert(buf);
}
ac.build();
getchar();
gets(buf);
ac.query(buf);
int len=strlen(buf),sum=0;
for(int i=0;iif(sum<=0) printf("%c",buf[i]);
else printf("*");
}
printf("\n");
}
return 0;
}
http://acm.hdu.edu.cn/showproblem.php?pid=5881
题目大意:给你一壶茶,你不知道茶有多少,只知道在[L,R]之间,给你两个杯子,要求两个杯子的茶水量差不超过1,杯子最后剩余的水不超过1,问至少倒几次。
题目分析:这题我有点不太理解,就直接贴下答案吧,因为他说至少倒几次是在[L,R]的最坏条件下还是最好条件下?如果考虑最好条件下,那么R值就没用了,倒两次就好了。还有就是你是不是只知道茶水什么时候剩1以下?
这道题的解释是这样的:
* 如果 R≤1 ,那么不用倒直接满足题意。
* 如果 R=2 ,那就倒1次,这里我有个问题,就是如果是[0,2]这个输入,那么你是有可能倒不出来的啊。。。。。。。
* 如果 R−L≤1 ,那么就倒两次,每次倒L/2,这样剩的肯定不超过1.
* 否则,先倒两次,分别是 (L−1)/2,(L+1)/2 ,再一边倒2.
#include
using namespace std;
typedef long long ll;
ll l,r;
int main() {
while(scanf("%I64d %I64d",&l,&r)!=EOF) {
ll ans;
if(r<=1)
ans=0;
else if(r<=2)
ans=1;
else if(r-l<=1)
ans=2;
else {
if(l<=1) l=1;
ans=(r-l)/2+1;
}
printf("%I64d\n", ans);
}
}
http://acm.hdu.edu.cn/showproblem.php?pid=5882
题目大意:
问n个手势的石头剪子布是否公平?
题目分析:
说实话这题我也没彻底理解,因为从直觉上看,就是奇数公平偶数不公平,而且偶数不公平显然如此。
but,如何证明奇数一定公平?有没有一种对任意n均公平的分配方案?这个有待进一步探讨啊~~
2016.11.16 cmershen add
知乎上有大神回复了,可以这样设计方案:
在圆周上等距离分布2n+1个点,对任意一个点,顺时针数n个点赢他,逆时针数n个点输他,这样就可以保证任意两种不同手势均能分出胜负,且完全公平。
#include
using namespace std;
typedef long long ll;
int t,n;
int main() {
scanf("%d",&t);
while (t--) {
scanf("%d",&n);
if (n&1)
printf("Balanced\n");
else
printf("Bad\n");
}
}
http://acm.hdu.edu.cn/showproblem.php?pid=5883
题目大意:n个点,m个边的无向图,每个点有权值 ai ,问可不可以每条边只走一遍,遍历所有边?如果可以,则求出路径上经过的所有点(可以重复)异或的最大值,如果不可以,输出Impossible
题目分析:
求欧拉路咯,小学奥数都学过,看有几个度为奇数的点。如果是0个,则是欧拉回路,如果是2个,则是欧拉通路。
接下来就看每个点要经过几次:
如果度为n,那么这n条边都要遍历一次,而每走其中两条边,就要经过这个点一次(因为要来到这个点,还得出去),如果剩下一条边,那么也要走一次这个点,因为它必是起点/终点。
根据异或的性质,如果这个次数值是奇数,那就加到答案里,如果是偶数,那就消掉了。
那么如果该图存在的是欧拉通路,那答案就是唯一的,如果是欧拉回路,相当于起点多走了一次,枚举起点即可。
这题的数据我觉得有点弱,因为还需要考虑整个图是否连通的问题,但是没考虑也过了。
#include
using namespace std;
typedef long long ll;
int t,n,m,u,v;
short a[100005];
int deg[100005];
vector<int> g[100005];
int main() {
scanf("%d",&t);
while (t--) {
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
g[i].clear();
memset(deg,0,sizeof(deg));
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=0;iscanf("%d %d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
deg[u]++;deg[v]++;
}
int cnt=0,sum=0;
for(int i=1;i<=n;i++) {
if(deg[i]&1)
cnt++;
int temp=(deg[i]+1)/2;
if(temp & 1)
sum^=a[i];
}
if(cnt==0) { //Eular Curcuit
int ans=0;
for(int i=1;i<=n;i++)
ans=max(ans,sum^a[i]);
printf("%d\n", ans);
}
else if(cnt==2) //Eular Path
printf("%d\n", sum);
else
printf("Impossible\n");
}
}
http://acm.hdu.edu.cn/showproblem.php?pid=5884
题目大意:
给你n个正整数和最大花费T,每次可以合并不超过k个数,花费是这k个数的和,最后合并成一个数,求不超过花费T的情况下,最小的k。
题目分析:
其实这就是一个k叉Huffman树求带权路径长度的问题。首先考虑到我们要用k-1次操作合并n-1个数,那么当
(k−1)%(n−1)!=0 时,最后剩下的数就会不到k个,所以我们先将多出来的部分合并成一个,显然先合并要比后合并花费小。
接下来就是求这个最小的k,因为k的取值范围确定为 [2,n] ,且花费 cost(k) 对k单调递减(这个我不知道咋证。。。直觉如此),因此在 [2,n] 上二分答案就ok了。
如果你就这么做下去,那么你肯定tle了。
这题卡了logn(大概在6左右),直接用优先队列的复杂度是 O(logn∗nlogn) ,因此要优化一下。
用两个队列q1和q2,其中q1装原来的数(排序好),q2装合并后的数,根据Huffman树性质,合并数肯定是越合越大,所以q2也是单调的。那么每次先从q1和q2里加一起取k个数,合并起来扔q2里,直到q1取完,再取q2,直到q2里剩一个数。这样做就能过了,时间在1s左右。
#include
using namespace std;
typedef long long ll;
int t0,t,n;
int a[100005];
int cost(int x) {
queue<int> q1,q2;
int ans=0;
sort(a,a+n);
for(int i=0;iif((n-1)%(x-1)!=0) {
int temp=(n-1)%(x-1)+1;
int sum=0;
for(int j=0;jwhile(!q1.empty()) {
int sum=0;
for(int j=0;jif(!q1.empty() && !q2.empty()) {
if(q1.front()else {
sum+=q2.front();
q2.pop();
}
}
else if(q1.empty()) {
sum+=q2.front();
q2.pop();
}
else if(q2.empty()) {
sum+=q1.front();
q1.pop();
}
}
ans+=sum;
q2.push(sum);
}
while(q2.size()>1) {
int sum=0;
for(int i=0;ireturn ans;
}
int main() {
scanf("%d",&t0);
while(t0--) {
scanf("%d %d",&n,&t);
for(int i=0;iscanf("%d",&a[i]);
}
int l=2,r=n;
while(lint mid=(l+r)/2;
if(cost(mid)<=t)
r=mid;
else
l=mid+1;
}
printf("%d\n", l);
}
}
http://acm.hdu.edu.cn/showproblem.php?pid=5887
题目大意:
01背包,100个物品,体积和背包容量可达1e9。
题目分析:
我们传统的用DP解背包都是物品多,背包容量M有限,这样可以用DP做,在 O(nm) 内就能搞定了。
但此题的M太大了,N又很小,所以用搜索来解。但 2100 的搜索空间也不行啊,所以剪枝吧~
这道题我见到的黑科技很多,最极端的是用时间剪枝,就是一个dfs跑到10ms就return,居然也能过。。。。。。。。
我采用的是按性价比剪枝,也就是先按性价比排序,如果背包剩下的空间里全装这个东西也没有更优解,则停止搜索(因为后面的性价比低,就更不可能找到解了),这样就0ms AC了。
#include
using namespace std;
#define RE(x) freopen(x,"r",stdin)
#define WR(x) freopen(x,"w",stdout)
typedef long long ll;
int n,t;
typedef struct {
int t;//time
int s;//score
double r;//rate
}herb;
herb h[105];
bool cmp(herb x,herb y) {
return x.r>y.r;
}
ll ans;
void dfs(int i,ll cur,ll sc) { //决定第i个物品放不放,当前背包容量为cur,得分为sc
if(sc>ans)
ans=sc;
if(sc+h[i].r*(t-cur)return;//剩下的容积就算全放i,也得不到更优解,更何况后面的性价比更低呢
if(iif(h[i].t<=t-cur) //第i个物品装得下
dfs(i+1,cur+h[i].t,sc+h[i].s);
dfs(i+1,cur,sc);//不放i
}
}
int main() {
RE("in.txt");
WR("out.txt");
while(scanf("%d %d",&n,&t)!=EOF) {
for(int i=0;iscanf("%d %d",&h[i].t,&h[i].s);
h[i].r=(double)(h[i].s)/(double)(h[i].t);
}
ans=0;
sort(h,h+n,cmp);
dfs(0,0,0);//决定第0个物品,目前背包里容量为0,得分为0
printf("%I64d\n", ans);
}
}
http://acm.hdu.edu.cn/showproblem.php?pid=5889
题目大意:
有个地图,n个点,m个边,无向图,每条边的长度都一样。
你在1点,敌人在n点,已知敌人一定按最短路线来找你,所以你要将敌人的路设置障碍,使得敌人不管沿什么路径过来,都会遇到你的障碍。又已知在第i条边设置障碍需要cost[i]的花费,求至少多少花费才能使敌人全部遇到障碍。
题目分析:
首先在原图上跑一遍dijkstra算法,求1点到其他点的最短路,然后构造一张新图,在最短路的路径上(条件是dis[i]-dis[j]==1 && i,j有边
)加边,边权为设置障碍的花费。然后新图上以1为源,n为汇点跑最大流即可。
这里分别给出dinic算法和SAP算法的AC代码,供当做模板用,其中sap算法用时15ms,dinic算法用时46ms。
//sap,15ms
#include
using namespace std;
#define RE(x) freopen(x,"r",stdin)
#define WR(x) freopen(x,"w",stdout)
typedef long long ll;
#define INF 0x3f3f3f3f
int t,u,v,w,n,m;
int cost[1005][1005];
int dis[1005];
struct EDGE {
int u,v,cap;
int next;
}edge[20005];
int head[1005],p;
int gap[1005],dep[1005],cur[1005],stk[1005];
void addedge(int u,int v) {
int c=cost[u][v];
edge[p].u=u;edge[p].v=v;edge[p].cap=c;
edge[p].next=head[u]; head[u]=p++;
edge[p].u=v;edge[p].v=u;edge[p].cap=0;
edge[p].next=head[v]; head[v]=p++;
}
void dijkstra(int u) { //u点为源,求单源最短路径
bool vis[1005];
for(int i=1;i<=n;i++) {
if(cost[i][u]!=INF && i!=u)
dis[i]=1;
else
dis[i]=INF;
vis[i]=false;
}
dis[u]=0;
vis[u]=true;
for(int i=1;i<=n;i++) {
int min=INF;
int x;
for(int j=1;j<=n;j++) {
if(!vis[j] && dis[j]true;
for(int j=1;j<=n;j++)
if(!vis[j] && cost[x][j]!=INF && min+11;
}
}
void bfs(int t) {
memset(dep,-1,sizeof(dep));
memset(gap,0,sizeof(gap));
queue<int> q;
dep[t]=0;
gap[0]=1;
q.push(t);
while(!q.empty()) {
int u=q.front();
q.pop();
for(int i=head[u];i!=-1;i=edge[i].next) {
int v=edge[i].v;
if(edge[i^1].cap>0 && dep[v]==-1) {
q.push(v);
dep[v]=dep[u]+1;
gap[dep[v]]++;
}
}
}
}
int sap(int s,int t) {
bfs(t);
memcpy(cur,head,sizeof(cur));
int ans=0;
int u=s,top=0,i;
while(dep[s]if(u==t) {
int delta=INF;
int flag=n;
for(i=0;i!=top;i++) {
if(delta>edge[stk[i]].cap) {
delta=edge[stk[i]].cap;
flag=i;
}
}
for(i=0;i!=top;i++) {
edge[stk[i]].cap-=delta;
edge[stk[i]^1].cap+=delta;
}
ans+=delta;
top=flag;
u=edge[stk[top]].u;
}
for(i=cur[u];i!=-1;i=edge[i].next) {
int v=edge[i].v;
if(edge[i].cap>0 && dep[u]==dep[v]+1)
break;
}
if(i!=-1) {
cur[u]=i;
stk[top++]=i;
u=edge[i].v;
}
else {
if(--gap[dep[u]]==0)
break;
int mind=n+1;
for(i=head[u];i!=-1;i=edge[i].next) {
if(edge[i].cap>0 && mind>dep[edge[i].v]) {
mind=dep[edge[i].v];
cur[u]=i;
}
}
dep[u]=mind+1;
gap[dep[u]]++;
u=(u==s)?u:edge[stk[--top]].u;
}
}
return ans;
}
int main() {
scanf("%d",&t);
while(t--) {
scanf("%d %d",&n,&m);
memset(cost,0x3f,sizeof(cost));
memset(head,-1,sizeof(head));
p=0;
for(int i=1;i<=m;i++) {
scanf("%d %d %d",&u,&v,&w);
cost[u][v]=w;
cost[v][u]=w;
}
dijkstra(1);
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) {
if(dis[i]+1==dis[j] && cost[i][j]!=INF)
addedge(i,j);
}
}
printf("%d\n", sap(1,n)); //源点1,汇点n,求最大流
}
}
//Dinic,46ms
#include
using namespace std;
#define RE(x) freopen(x,"r",stdin)
#define WR(x) freopen(x,"w",stdout)
typedef long long ll;
#define INF 0x3f3f3f3f
int t,u,v,w,n,m;
int cost[1005][1005];
int dis[1005];
struct EDGE {
int to,next,flow;
}edge[20005];
int head[10005],p,level[10005];
void addedge(int u,int v) {
int w=cost[u][v];
edge[p].to=v;
edge[p].flow=w;
edge[p].next=head[u];
head[u]=p;p++;
edge[p].to=u;
edge[p].flow=0;
edge[p].next=head[v];
head[v]=p;p++;
}
void dijkstra(int u) { //u点为源,求单源最短路径
bool vis[1005];
for(int i=1;i<=n;i++) {
if(cost[i][u]!=INF && i!=u)
dis[i]=1;
else
dis[i]=INF;
vis[i]=false;
}
dis[u]=0;
vis[u]=true;
for(int i=1;i<=n;i++) {
int min=INF;
int x;
for(int j=1;j<=n;j++) {
if(!vis[j] && dis[j]true;
for(int j=1;j<=n;j++)
if(!vis[j] && cost[x][j]!=INF && min+11;
}
}
bool bfs(int s,int t) {
memset(level,0,sizeof(level));
queue<int> q;
q.push(s);
level[s]=1;
while(!q.empty()) {
int u=q.front();
q.pop();
if(t==u)
return true;
for(int i=head[u];i!=-1;i=edge[i].next) {
int v=edge[i].to,f=edge[i].flow;
if(level[v] == 0 && f!=0) {
q.push(v);
level[v]=level[u]+1;
}
}
}
return false;
}
int dfs(int u,int maxf,int t) {
if(u==t)
return maxf;
int temp=0;
for(int i=head[u];i!=-1&&tempint v=edge[i].to,f=edge[i].flow;
if(level[v] == level[u]+1 && f!=0) {
f=dfs(v,min(maxf-temp,f),t);
edge[i].flow-=f;edge[i^1].flow+=f;temp+=f;
}
}
if(!temp)
level[u]=INF;
return temp;
}
int dinic(int s,int t) { //源点s,汇点t,求最大流
ll ans=0;
while(bfs(s,t))
ans+=dfs(s,INF,t);
return ans;
}
int main() {
scanf("%d",&t);
while(t--) {
scanf("%d %d",&n,&m);
memset(cost,0x3f,sizeof(cost));
memset(head,-1,sizeof(head));
p=0;
for(int i=1;i<=m;i++) {
scanf("%d %d %d",&u,&v,&w);
cost[u][v]=w;
cost[v][u]=w;
}
dijkstra(1);
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) {
if(dis[i]+1==dis[j] && cost[i][j]!=INF)
addedge(i,j);
}
}
printf("%d\n", dinic(1,n)); //源点1,汇点n,求最大流
}
}