HDU-4340-treeDP
这题一看就是树DP。可是当时我想错了。伤心死了。。后来看到解题报告 dp[N][2][2], 瞬间就都懂了 。。 可是没用了 。。 没用了 。。
dp[i][2][2] //一维表示以谁为节点的子树; 二维第几个人; 三位这棵子树有几个全价。
转移方程,以第0个人为例,同理第1个人。j,k是i的不同孩子。
dp[i][0][0] = cost[i][0]/2 + sum( min( dp[j][0][0], dp[j][1][1] ) );
dp[i][0][1] = cost[i][0] + sum( min( dp[j][0][0], dp[j][1][1] ) );
dp[i][0][1] = cost[i][0]/2 + min( dp[k][0][1], sum( dp[j][0][0], dp[j][1][1] ) );
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <algorithm>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 1009;
const double esp = 1e-6;
const int N = 1009;
vector<int>v[N];
int dp[N][2][2];
int a[N][2];
int n;
void dfs(int s, int pre)
{
int i, ss, sum0=0, sum1=0, tmp0=1<<20, tmp1=1<<20;
for(i=0; i<v[s].size(); i++)
{
ss = v[s][i];
if(ss==pre) continue;
dfs(ss, s);
sum0 += min( dp[ss][0][0], dp[ss][1][1] );
sum1 += min( dp[ss][1][0], dp[ss][0][1] );
tmp0 = min( tmp0, dp[ss][0][1]-min( dp[ss][0][0], dp[ss][1][1] ) );
tmp1 = min( tmp1, dp[ss][1][1]-min( dp[ss][1][0], dp[ss][0][1] ) );
}
dp[s][0][0] = a[s][0]/2 + sum0;
dp[s][1][0] = a[s][1]/2 + sum1;
dp[s][0][1] = min( a[s][0]+sum0, a[s][0]/2+tmp0+sum0 );
dp[s][1][1] = min( a[s][1]+sum1, a[s][1]/2+tmp1+sum1 );
}
int main()
{
int i, x, y;
while(~scanf("%d", &n))
{
for(i=1; i<=n; i++) scanf("%d", &a[i][0]);
for(i=1; i<=n; i++) scanf("%d", &a[i][1]);
for(i=1; i<=n; i++) v[i].clear();
for(i=1; i<n; i++)
{
scanf("%d%d", &x, &y);
v[x].push_back(y);
v[y].push_back(x);
}
dfs(1, 0);
printf("%d\n", min(dp[1][1][1], dp[1][0][1]));
}
return 0;
}
HDU-4341-分组背包
排序(先取近的),把共线的点分为一组,然后就是分组背包了 。。。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <vector>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int maxn = 209;
const int maxt = 40009;
int dp[maxt];
int N, T;
vector<int>g[maxn];
struct point
{
int x, y, t, v, grp;
}p[maxn];
int cmp(point a, point b)
{
return a.x<b.x;
}
int main()
{
int ca = 0, cnt, i, j, k;
while(~scanf("%d%d", &N, &T))
{
for(i=1; i<=N; i++)
{
scanf("%d%d%d%d", &p[i].y, &p[i].x, &p[i].t, &p[i].v);
p[i].grp = 0;
g[i].clear();
}
sort(p+1, p+N+1, cmp);
p[1].grp = cnt = 1;
g[1].push_back(1);
for(i=1; i<=N; i++)
{
for(j=i+1; j<=N; j++)
if(p[j].grp==0)
{
if( p[i].x*p[j].y == p[i].y*p[j].x )
{
p[j].grp = p[i].grp;
g[ p[i].grp ].push_back(j);
}
}
if(i<N && p[i+1].grp==0)
{
p[i+1].grp = ++cnt;
g[cnt].push_back(i+1);
}
}
memset(dp, 0, sizeof(dp));
for(i=1; i<=cnt; i++) //groups
{
for(k=T; k>=0; k--)
{
int t = 0, v = 0;
for(j=0; j<g[i].size(); j++)
{
int id = g[i][j];
t += p[id].t;
v += p[id].v;
if(k>=t) dp[k] = max(dp[k], dp[k-t]+v);
else break;
}
}
}
printf("Case %d: %d\n", ++ca, dp[T]);
}
return 0;
}
HDU-4342-
求第n个非平方数,和大于等于这个数的开方和。n为int型。
这题不会,看的别人的解题思路。
http://blog.youkuaiyun.com/wmn_wmn/article/details/7840712
HDU-4343-倍增算法
排序,去掉覆盖其他点的线,离散化思想。然后变成s和t都是上升的序列。
prev[x][k] 表示 第x个点,向前2^k个不重复线的最后位置。
如 prev[x][0] = max( y(y<x && t[y]<=s[x]) )
递推公式: prev[x][k] = prev[prev[x][k-1]][k-1];
对询问排序,二分模拟答案。
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int INF = 1000000001;
struct line
{
line() {}
line(int a, int b): s(a), t(b) {}
int s, t;
bool vis;
bool operator<(const line &a)const
{
return s<a.s;
}
}li[100010], aft[100010];
bool cmp(const line &u, const line &v)
{
if (u.t==v.t) return u.s>v.s;
return u.t<v.t;
}
struct sq
{
int id;
int s, t;
bool operator<(const sq &a) const
{
return t<a.t;
}
}q[100010];
int prev[100010][22];
int ans[100010];
int N, M;
int main()
{
int i, j, k;
while (~scanf("%d%d", &N, &M))
{
for (i = 0; i < N; ++i)
{
scanf("%d%d", &li[i].s, &li[i].t);
li[i].vis = 0;
}
sort(li, li+N, cmp);
for (i = 0; i < N; )
{
j = i+1;
while (j<N && li[j].s <= li[i].s) li[j++].vis = 1;
i = j;
}
j = 0;
for (i = 0; i < N; ++i)
if (!li[i].vis) aft[++j] = li[i];
N = j;
sort(aft+1, aft+N+1);
aft[0] = line(-2, -1);
for (i=0,j=1; j<N; ++i,j<<=1);
int bmax = i;
for (k = i = 1; i <= N; ++i)
{
while (aft[k].t <= aft[i].s) k++;
prev[i][0] = k-1;//i之前和i最近的不重叠的线
}
prev[0][0] = 0;
for (j = 1; j <= bmax; ++j)
{
prev[0][j] = 0;
for (i = 1; i <= N; ++i)
{
prev[i][j] = prev[prev[i][j-1]][j-1];
}
}
for (i = 0; i < M; ++i)
{
scanf("%d%d", &q[i].s, &q[i].t);
q[i].id = i;
}
sort(q, q+M);
k = 0;
int b;
for (i = 0; i < M; ++i)
{
while (k<=N && aft[k].t <= q[i].t) k++;
int &res = ans[q[i].id], now = k-1;
res = 0;
for (j=bmax; j>=0; j--)
{
if (aft[prev[now][j]].s>=q[i].s)
{
res |= (1<<j);
now = prev[now][j];
}
}
if (aft[k-1].s >= q[i].s) res++;
}
for (i = 0; i < M; ++i)
printf("%d\n", ans[i]);
}
return 0;
}
HDU-4344-大数素数分解
不知怎么描述题意。如198=2*3*3*11,则ans(198)=2+9+11=22; 8=2*2*2 而ans(8)=4
这题到现在也不明白,贴个代码当作大数分解的模版吧。(随机数)
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
#define MAXN 10
__int64 minn;
__int64 multi(__int64 a,__int64 b,__int64 n)
{
__int64 temp=a%n,s=0LL;
while(b)
{
if(b&1LL)s=(s+temp)%n;
temp=(temp+temp)%n;
b>>=1;
}
return s;
}
__int64 Pow(__int64 a,__int64 b,__int64 n)
{
__int64 temp=a%n,s=1LL;
while(b)
{
if(b&1LL)s=multi(s,temp,n);
temp=multi(temp,temp,n);
b>>=1;
}
return s;
}
__int64 Pow2(__int64 a,__int64 b)
{
__int64 s = 1LL;
for(int i = 0; i < b; ++i)
s *= a;
return s;
}
int witness(__int64 a,__int64 n)
{
__int64 u=n-1LL,t=0LL,i,x,y;
while(!(u&1LL))u>>=1,t++;
x=Pow(a,u,n);
for(i=1;i<=t;i++)
{
y=multi(x,x,n);
if(y==1LL && x!=1LL && x!=n-1LL)return 1;
x=y;
}
if(x!=1LL) return 1;
return 0;
}
int test(__int64 n)
{
__int64 a;
int i;
if(n==2LL) return 1;
if(n<2LL||!(n&1ll))return 0;
for(i=0;i<MAXN;i++)
{
a=(__int64) rand()%(n-2)+2;
if(witness(a,n))return 0;
}
return 1;
}
__int64 gcd(__int64 a,__int64 b)
{
return b?gcd(b,a%b):a;
}
__int64 pollard_rho(__int64 n,__int64 c)
{
__int64 x, y, d, i=1LL, k=2LL;
x=((__int64)rand())%(n-1LL)+1LL;
y=x;
while(1)
{
++i;
x=(multi(x,x,n)+c)%n;
d=gcd(y-x+n,n);
if(d!=1 && d!=n) return d;
if(x==y)return n;
if(i==k)
{
y=x;
k<<=1;
}
}
}
void find(__int64 n,__int64 c = 70ll)
{
__int64 r;
if(n<=1LL) return ;
if(test(n))
{
if(minn>n)minn=n;
return ;
}
r=pollard_rho(n,c--);
find(n/r,c);
find(r,c);
}
__int64 top;
__int64 vec[1000000][2];
__int64 pri[] = {2,3,5,7,11,13,17,19,23,29};
int main()
{
__int64 n,ans,cnt;
int t,i;
scanf("%d",&t);
while(t--)
{
top = 0;
scanf("%I64d",&n);
for(i = 0; i < 10; ++i)
{
cnt = 0;
while(n % pri[i] == 0)
{
++cnt;
n /= pri[i];
}
if(cnt) vec[top][0] = pri[i],vec[top++][1] = cnt;
}
while(n!=1)
{
minn=n;
find(n);
cnt =0;
while(n % minn == 0)
{
++cnt;
n /= minn;
}
vec[top][0] = minn,vec[top++][1] = cnt;
}
ans = 0;
if(top == 1) vec[0][1] --;
for(i = 0; i < top; ++i) ans += Pow2(vec[i][0],vec[i][1]);
printf("%I64d %I64d\n",top, ans);
}
return 0;
}
HDU-4345-记忆化DP 求循环节
当时怎么就没想到呢 。。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int inf = 0x3f3f3f3f;
const int N = 1100;
const double esp = 1e-6;
int pri[N], vis[N];
__int64 ans[N][N];
int cnt;
void prime()
{
int i, j;
cnt = 0;
for(i=2; i<N; i++)
if(!vis[i])
{
pri[cnt++] = i;
for(j=i+i; j<N; j+=i) vis[j] = 1;
}
}
__int64 dp(int m, int n, int i)
{
if(ans[n][i]!=-1) return ans[n][i];
if(i>m) return ans[n][i] = 1;
ans[n][i] = dp(m, n, i+1);//首先是第i个质数不存在的情况
int k = pri[i];
while(k<=n)
{
ans[n][i] += dp(m, n-k, i+1);//然后枚举他的幂次数存在
k *= pri[i];
}
return ans[n][i];
}
int main()
{
int i, n, m;
prime();
while(~scanf("%d", &n))
{
for(i=0; pri[i]<=n; i++);
m = i-1;
memset(ans, -1, sizeof(ans));
printf("%I64d\n", dp(m, n, 0));
}
return 0;
}
HDU-4348-线段树
离线线段树 。。 解题报告中的做法都不会 。。 T_T
离线,两个原因。
1.查询某历史事件的都单独存起来,到那个时间就求一下, 而不等到真正查询的时候再求。
2.回到某一历史事件。把前面的各种操作都存起来,回去的时候,把修改操作反修改一边。这里用一个to[]数组表示第i条操作的前一条。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int N = 100009;
__int64 val[N<<2], lazy[N<<2], ans[N];
int p[N], to[N];
struct que
{
char op[3];
int l, r, t;
__int64 d;
}q[N];
bool cmp(int a, int b)
{
return q[a].t<q[b].t;
}
void build(int l, int r, int k)
{
lazy[k] = 0;
if(l==r)
{
scanf("%I64d", &val[k]);
return;
}
int mid = (l+r)>>1;
build(l, mid, k<<1);
build(mid+1, r, k<<1|1);
val[k] = val[k<<1] + val[k<<1|1];
}
void updata(int l, int r, __int64 d, int ll, int rr, int k)
{
val[k] += d*(r-l+1);
if(l==ll && r==rr)
{
lazy[k] += d;
return;
}
int mid = (ll+rr)>>1;
if(r<=mid) updata(l, r, d, ll, mid, k<<1);
else if(l>mid) updata(l, r, d, mid+1, rr, k<<1|1);
else
{
updata(l, mid, d, ll, mid, k<<1);
updata(mid+1, r, d, mid+1, rr, k<<1|1);
}
}
void down(int ll, int rr, int k)
{
int mid = (ll+rr)>>1;
val[k<<1] += lazy[k]*(mid-ll+1);
val[k<<1|1] += lazy[k]*(rr-mid);
lazy[k<<1] += lazy[k];
lazy[k<<1|1] += lazy[k];
lazy[k] = 0;
}
__int64 query(int l, int r, int ll, int rr, int k)
{
if(lazy[k]!=0)
{
if(ll==rr) lazy[k] = 0;
else down(ll, rr, k);
}
if(l==ll && rr==r) return val[k];
int mid = (ll+rr)>>1;
if(r<=mid) return query(l, r, ll, mid, k<<1);
else if(l>mid) return query(l, r, mid+1, rr, k<<1|1);
else return query(l, mid, ll, mid, k<<1) + query(mid+1, r, mid+1, rr, k<<1|1);
}
int find(int l,int r,int t)
{
int m;
while(l<=r)
{
m=(l+r)>>1;
if(q[p[m]].t>=t) r=m-1;
else l=m+1;
}
return r;
}
int main()
{
int n, m, i, j;
while(~scanf("%d%d", &n, &m))
{
build(1, n, 1);
int h=0;
for(i=0; i<m; i++)
{
scanf("%s", q[i].op);
switch(q[i].op[0])
{
case 'C':scanf("%d%d%I64d", &q[i].l, &q[i].r, &q[i].d); break;
case 'Q':scanf("%d%d", &q[i].l, &q[i].r); break;
case 'H':scanf("%d%d%d", &q[i].l, &q[i].r, &q[i].t); p[h++]=i; break;
case 'B':scanf("%d", &q[i].t); break;
}
to[i] = i-1; //初始就是他的前一位
}
sort(p, p+h, cmp); //按时间排序
int t = 0, hh = 0;
for( ; hh<h; hh++)
{
int id = p[hh];
if(q[id].t < t) continue;
else if( q[id].t == t ) ans[id] = query( q[id].l, q[id].r, 1, n, 1 ) ;
else break;
}
for(i=0; i<m; i++) //逐条操作
{
if(q[i].op[0]=='C')
{
updata(q[i].l, q[i].r, q[i].d, 1, n, 1);
t++;
for(hh=find(i+1, h-1, t) ; hh<h; hh++)
{
int id = p[hh];
if(q[id].t < t) continue;
else if( q[id].t == t ) ans[id] = query( q[id].l, q[id].r, 1, n, 1 ) ;
else break;
}
}
else if(q[i].op[0]=='Q') printf("%I64d\n", query(q[i].l, q[i].r, 1, n, 1));
else if(q[i].op[0]=='H') printf("%I64d\n", ans[i]);
else
{
j = to[i];
while(j>=0 && t>q[i].t)
{
if(q[j].op[0]=='C') updata(q[j].l, q[j].r, -q[j].d, 1, n, 1), --t;
j = to[j];
}
to[i] = j;
for(hh=find(i+1, h-1, t) ; hh<h; hh++)
{
int id = p[hh];
if(q[id].t < t) continue;
else if( q[id].t == t ) ans[id] = query( q[id].l, q[id].r, 1, n, 1 ) ;
else break;
}
}
}
}
return 0;
}
705

被折叠的 条评论
为什么被折叠?



