----------------
A. Magic Numbers
一个神奇的数字是由1、14、144连接而成的,判断一个数字是不是神奇数字。
----
①没有连续3个以上的4。
②首位不能为4。
数据范围太大,最好按字符读入。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
char c;
int n,num;
bool flag;
int main()
{
flag=true;
num=0;
n=0;
while (cin>>c)
{
if (n==0&&c!='1')
{
flag=false;
break;
}
if (c!='1'&&c!='4')
{
flag=false;
break;
}
if (c=='4') num++;
else num=0;
if (num>2)
{
flag=false;
break;
}
n++;
}
if (flag) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
return 0;
}
----------------
B. Ping-Pong (Easy Version)
两个数对(a,b)、(c,d),若c < a < d or c < b < d 则数对(a,b)到数对(c,d)存在一条有向边。
① 1 a b 表示添加数对(a,b)
② 2 a b 询问是否有一条路径由第a个数对到第b个数对。
----
按要求建边,dfs判断是否连通即可。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n;
bool a[111][111];
int x[111],y[111];
bool vis[111]={0};
bool dfs(int u,int v)
{
vis[u]=true;
if (u==v) return true;
for (int i=0;i<n;i++)
if (a[u][i]&&!vis[i])
if (dfs(i,v)) return true;
return false;
}
int main()
{
int T;
n=0;
memset(a,0,sizeof(a));
cin>>T;
while (T--)
{
int t;
cin>>t;
if (t==1)
{
cin>>x[n]>>y[n];
for (int i=0;i<n;i++)
{
if ((x[i]<x[n]&&x[n]<y[i])||(x[i]<y[n]&&y[n]<y[i])) a[n][i]=true;
if ((x[n]<x[i]&&x[i]<y[n])||(x[n]<y[i]&&y[i]<y[n])) a[i][n]=true;
}
n++;
}
if (t==2)
{
int x,y;
cin>>x>>y;
memset(vis,0,sizeof(vis));
if (dfs(x-1,y-1)) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
}
return 0;
}
----------------
C. Malek Dance Club
A组织的2^n个人与B组织的2^n配对跳舞。
配对规则为一个长度为n的二进制串x。
A组织的第i个人与B组织的第(i异或x)个人跳舞。
若有(a, b) (c, d)配对,若 a < c and b > d. 则有一个配对的复杂度。
给出x,问两个组织配对跳舞的复杂度。结果mod1000000007 。
----
有图有真相。。。
图中左右两侧的点为A、B两组织的人员i的二进制编号,上方的x为异或用的二进制串。
在配对的两个人之间连一条线,则配对复杂度即为交点的个数。
假设已经解决了二进制串x,现在要计算1x和0x的值。
此时已经计算出了f(x)的值。
由图观察可知:
①f(0x)=2*f(x)。
(设左右侧原来的编号为e,现在编号变为1e和0e,左0e和右0e配对,左1e和右1e配对,交点变为原来的2倍)
(而左侧0e和右侧1e之间并没有增加新交点,总交点数变为以前的2倍)
②f(1x)=2*f(x)+4^n。
(编号变为1e和0e交点变为以前的2倍,而左0e与右1e配对,左1e与右0e配对,左0e与右0e产生了2^n个新交点,同理左1e与右1e产生了2^n个新交点)
(共增加了4^n个交点)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <string>
using namespace std;
const int maxn=111;
const int MOD=1000000007;
long long f[maxn];
int n;
string x;
long long pw[maxn];
int main()
{
pw[0]=1;
for (int i=1;i<=100;i++)
{
pw[i]=pw[i-1]*4%MOD;
}
while (cin>>x)
{
n=x.length();
f[n]=0;
for (int i=n-1;i>=0;i--)
{
if (x[i]=='0') f[i]=2*f[i+1];
if (x[i]=='1') f[i]=2*f[i+1]+pw[(n-i-1)];
f[i]%=MOD;
}
cout<<f[0]<<endl;
}
return 0;
}
----------------
D. Psychos in a Line
n个人排成1排,每个人有一个SAN值。
每一回合,若一个人SAN值大于右边人的SAN值,则右边的人被杀。
所有杀害同时发生。剩下的人重新排列。
问多少回合后没有杀害发生。
----
题解暂无。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
#include <queue>
#include <cmath>
using namespace std;
const int maxn=111111;
const int INF=1e9+7;
int f[maxn];
int a[maxn];
int n;
stack<int>stk;
int main()
{
int ans;
while (cin>>n)
{
ans=0;
while (!stk.empty()) stk.pop();
memset(f,0,sizeof(f));
for (int i=1;i<=n;i++) cin>>a[i];
f[0]=INF;
stk.push(0);
for (int i=1;i<=n;i++)
{
f[i]=1;
while (!stk.empty()&&a[i]>a[stk.top()])
{
f[i]=max(f[i],f[stk.top()]+1);
stk.pop();
}
stk.push(i);
if (f[i]<INF) ans=max(ans,f[i]);
}
cout<<ans<<endl;
}
return 0;
}
----------------
E. Kalila and Dimna in the Logging Industry
Kalila和Dimna两个豺狼生活在一个巨大的丛林。有一天,他们为了赚钱加入了一个伐木厂。
伐木场的经理希望他们去树林里砍高度为a1,a2,a3…an的n课树。他们从一家商店里买了一台链锯。每次在编号为i的树上使用链锯,他们都能减少这棵树的一个单位高度。每次Kalila和Dimna使用链锯,他们需要重新对它充电。充电的成本依赖于已经被完全切断的树的编号id(一棵树高度为0时被完全切断)。如果被完全切断的树最大的编号为i(树一开始高度为ai),那么对链锯充电的成本为bi。如果没有树被完全切断,Kalila和Dimna不能对链锯充电。链锯在伐木开始之前已经充电。我们已知对于每个i<j,ai<aj并且bi>bj,而且bn=0、a1=1。Kalila和Dimna希望用最低的成本砍完所有的树木。
他们希望你来帮助他们,你会吗?
INPUT
第一行包含一个整数n (1 ≤ n ≤ 105),第二行包含n个整数a1,a2,a3…an (1 ≤ ai ≤ 109),第三行包含n个整数,b1,b2,b3….bn (0 ≤ bi ≤ 109)。
保证a1=1,bn=0,a1<a2<...<an并且b1>b2>…>bn
OUTPUT
输出只有一行,砍完所有树木的最低成本。
----斜率DP
令f[i]为完全砍断第i棵树的最低成本。
如果最后一棵被砍断的树的编号为j,则f[i]=f[j]+a[i]*b[j]。
因此f[i]=min(f[j]+a[i]*b[j]) ( j<i )
简单dp时间复杂度为O(N*N)会超时,使用斜率优化可以变为O(N)的复杂度。
/*
//f[i]=min(f[j]+a[i]*b[j]) j<i
//-a[i]*b[j]+f[i]=f[j]
//令f[j]为y,b[j]为x,-a[i]为k,截距为dp[i]
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
using namespace std;
typedef long long LL;
const int maxn=111111;
LL a[maxn],b[maxn],f[maxn];
int n;
int que[maxn*4];
int head,tail;
//y1-y2
LL fun(int k1,int k2){
return f[k1]-f[k2];
}
//y1-y2>=k*(x1-x2)
bool cmp_head(int k1,int k2,int i){
return fun(k1,k2)>=-a[i]*(b[k1]-b[k2]);
}
//y1-y2/x1-x2>=y2-yi/x2-xi
bool cmp_tail(int k1,int k2,int i){
return double(fun(k1,k2))/double(b[k1]-b[k2])>=double(fun(k2,i))/double(b[k2]-b[i]);
}
//y-kx
LL dp_sol(int i,int j){return f[j]+a[i]*b[j];}
//y1-y2/x1-x2
double k_sol(int i,int j){return double(f[i]-f[j])/double(b[i]-b[j]);}
int main()
{
while (cin>>n)
{
memset(f,0,sizeof(f));
for (int i=0;i<n;i++) cin>>a[i];
for (int i=0;i<n;i++) cin>>b[i];
head=tail=0;
que[tail++]=0;
f[0]=0;
for (int i=1;i<n;i++)
{
while (tail-head>1&&dp_sol(i,que[head])>=dp_sol(i,que[head+1])) head++;
//while (tail-head>1&&cmp_head(que[head],que[head+1],i)) head++;
f[i]=dp_sol(i,que[head]);
while (tail-head>1&&k_sol(i,que[tail-1])>=k_sol(que[tail-1],que[tail-2])) tail--;
//while (tail-head>1&&cmp_tail(que[tail-2],que[tail-1],i) ) tail--;
que[tail++]=i;
}
cout<<f[n-1]<<endl;
}
return 0;
}
----------------