ZOJ Monthly, July 2012

zoj 3622、Magic Number  

水题

先找规律生成区间[1,1<<32-1]内的所有Magic Number,计算出来只有40多个,然后就随便YY了。

void init()
{
	int a[5] = {
		1,2,5,25,125
	};
	ll Max = (1ll<<32)-1;
	for(ll tmp =1; tmp<=Max; tmp *=10)
	{
		for(int i=0; i<5; ++i){
			ll t = tmp * a[i];
			if(t>Max) break;
			g.push_back(t);
		}
	}
	sort(g.begin(), g.end());
}

zoj 3623、Battle Ships  

完全背包

dp[i] 表示is时,能消耗防御塔的值。

状态转移方程:dp[j] = max(dp[j-t[i]] + l[i] * ( j – t[i]))

for(int i=0; i<N; ++i)
			for(int j=t[i]; j<=L+1; ++j)
				dp[j] = max(dp[j], dp[j-t[i]] + l[i]*(j-t[i]));

zoj 3624、Count Path Pair  

题意:从A到D与从B到C的路径中不相交的有多少条。

这题从反面考虑:

Answer   =   SUM(总的情况:C(m+n,m)*C(q+m-p,q) )  -  (A->D 与 B->C路径相交的情况)

然后重点分析:

(A->D 与 B->C路径相交的情况)

看下图:

发现:所有A->D 与B->C相交的情况都可以转化为A->C 与B->D的情况。

所以:Answer  = ( C(m + n, n) * C(m + q - p, q) - C(m + q, q) * C(n + m - p, n)) % 100000007。

注意:计算组合C(n,m)时,由于在计算阶乘的时候用了取模(不取模会溢出),在最后计算(n*...*n-m+1)/ m!时得到将非常不一定是正确结果,所以在这一步中我们可以采用 乘法逆

转化为  (n*..n-m+1) * inv(m!, mod).这样就不会出问题了。

void gcd(ll a, ll b, ll& d, ll& x, ll& y){
	if(!b){
		d = a; x = 1; y = 0;
	}
	else {
		gcd(b, a%b, d, y, x); y -= x*(a/b);
	}
}

ll inv(ll a, ll n){
	ll d, x, y;
	gcd(a, n, d, x, y);
	return d == 1 ? (x+n)%n : -1;
}

ll C(ll n, ll m){
	ll t1 = 1, t2 = 1;
	for(ll i=1; i<=m; ++i){
		t1 =(t1*(n-i+1)) % mod;
		t2 = (t2*i)% mod;
	}
	return t1*inv(t2, mod) % mod;
}

zoj 3625、Geek‘s Collection  

不会....

zoj 3626、Treasure Hunt I  

树形DP

给定n个点的一棵树,点有点权,边有边权,要求从某点出发,再回到原点能获得的最多点权。

把点权作为价值,边权作为消耗,就是一个树形背包。

把总能量m /= 2 ,就不用考虑再回到原点的部分。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;

const int maxn = 300;

int dp[maxn+10][maxn+10];
struct node {
    int v,t;
    node(int v=0, int t=0):v(v),t(t) {}
};

vector<node> g[maxn];
int v[maxn];
int n, k , m;

int dfs(int u, int pre)
{
    dp[u][0] = v[u];
    for(int i=0; i<g[u].size(); ++i) {
        node &e = g[u][i];
        if(e.v==pre) continue;
        dfs(e.v, u);
        for(int j=m; j>=e.t; --j) {
            for(int p=j; p>=e.t; --p) {
                dp[u][j] = max(dp[u][j], dp[u][j-p]+dp[e.v][p-e.t]);
            }
        }
    }
}

int main()
{
    //freopen("in.txt","r",stdin);
    while(~scanf("%d", &n)) {
        for(int i=1; i<=n; ++i) {
            scanf("%d", &v[i]);
        }
        int x, y, z;
        for(int i=0; i<=n; ++i) g[i].clear();
        for(int i=1; i<n; ++i) {
            scanf("%d%d%d", &x, &y, &z);
            g[x].push_back(node(y,z));
            g[y].push_back(node(x,z));
        }
        scanf("%d%d", &k, &m);
        m /= 2;
        memset(dp, 0, sizeof dp );
        dfs(k, -1);
        int ans = 0;
        for(int i=0; i<=m; ++i) ans = max(ans, dp[k][i]);
        printf("%d\n", ans);
    }
    return 0;
}

zoj 3627、Treasure Hunt II 

贪心

因为每个点的黄金只能走一次,而且有有时间、距离限制,那么一开始的时候肯定可以一起往两边跑,看到黄金就抢,直到超过距离或者时间不够或者到边界不能跑为止。这时候是双核,这样跑肯定最优。然后开始要么往左边一直跑,右边的跟着一直跑,遇到左边界就往右跑,要么就往右边一直跑,左边的跟着一直跑,遇到右边界就往左跑。如此这般就过掉了这题。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <cmath>
#include <set>
#include <map>
using namespace std;
typedef long long ll;

const int maxn = 100010;
int n, m, t, p;

ll val[maxn];
ll sum[maxn];

ll func(ll *sum, int p)
{
    ll ans = val[p];
    for(int r=min(n, p+t); r >= p; --r) {
        int l = max(1, r- m);
        l = max(p - t, l);
        int res = t - max(p - l, (r - p));
        int l1 = max(1, l - res);
        int r1 = min(n, r + res);
        ans = max(ans, max(sum[r]-sum[l1-1], sum[r1]-sum[l-1]));
    }
    return ans;
}

int main()
{
    while(~scanf("%d%d", &n, &p)) {
        sum[0] = 0;
        for(int i=1; i<=n; ++i) {
            cin >> val[i];
            sum[i] = sum[i-1] + val[i];
        }
        scanf("%d%d", &m, &t);
        ll ans = func(sum, p);
        for(int i=1; i<=n/2; ++i)
            swap(val[i], val[n+1-i]);
        for(int i=1; i<=n; ++i)
            sum[i] = sum[i-1] + val[i];
        ans = max(ans, func(sum, n+1-p));
        cout<<ans<<endl;
    }
    return 0;
}

zoj 3628、Treasure Hunt III

DP

不会。。。待补

zoj 3629、Treasure Hunt IV

找规律

http://blog.csdn.net/u010304217/article/details/38903983

zoj 3630、Information 

Tarjan

一个有向图,求删除一个点后,是得所有联通分量中点的数量得最大值,最小。

//求图中去掉一个点,求剩余的最大的联通分量中的点的数量。
//删去某点后,最大点数 最小是多少
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <stack>
#include <iostream>
using namespace std;

const int maxn = 110;

vector<int> G[maxn];
int dfn[maxn], low[maxn], belong[maxn], dfs_clock, scc_cnt;
stack<int> S;
int n, m, ans, del, cnt, ct;

void dfs(int u){
    dfn[u] = low[u] = ++dfs_clock;
    S.push(u);
    for(int i=0; i<G[u].size(); ++i){
        int v = G[u][i];
        if(v == del) continue;
        if(!dfn[v]){
            dfs(v);
            low[u] = min(low[u], low[v]);
        }else if(!belong[v]){
            low[u] = min(low[u], dfn[v]);
        }
    }
    if(low[u] == dfn[u]){
        scc_cnt++;
        int s = 0;
        for(;;){
            int x = S.top(); S.pop();
            belong[x] = scc_cnt;
            s++;
            if(x == u) break;
        }
        if(s>1&&s>ct) ct = s;
    }
}

void find_scc(int n){
    dfs_clock = scc_cnt = 0;
    memset(belong, 0, sizeof belong );
    memset(dfn, 0, sizeof dfn );
    cnt = 0;
    for(int i=0; i<n; ++i){
        ct = 0;
        if(!dfn[i]&& i!=del) dfs(i);
        cnt = max(cnt, ct);
    }
}

int main()
{
      while(~scanf("%d%d", &n, &m))
      {
            for(int i=0; i<=n; ++i) G[i].clear();
            int x, y;
            for(int i=0; i<m; ++i){
                  scanf("%d%d", &x, &y);
                  G[x].push_back(y);
            }

            ans = 1e8;
            while(!S.empty()) S.pop();
            for(del = 0; del<n; ++del)
            {

                  find_scc(n);
                  ans = min(ans, cnt);
            }
            printf("%d\n", ans);
      }
      return 0;
}

zoj 3631、Watashi‘s BG  

搜索+剪枝

#include <cstdio>
#include <algorithm>
using namespace std;
int n, m;
int a[35];
int s[360];
int Max;

void dfs(int x, int y) //dfs(i, money)
{
      if(y>m) return ;
      Max = max(y, Max);
      if(y+s[x]<=Max) return ; //剪纸
      if(y+a[x]<=m) dfs(x-1, y+a[x]);
      dfs(x-1, y);
}

int main()
{
    int i;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(i=1;i<=n;i++)scanf("%d",a+i);
        sort(a+1,a+n+1);
        s[0]=0; Max=0;
        for(i=1;i<=n;i++)s[i]=s[i-1]+a[i];
        dfs(n,0);
        printf("%d\n",Max);
    }
    return 0;
}

zoj 3632、Watermelon Full of Water  

DP+线段树优化

不会。。。待补

时间: 2024-10-02 23:48:28

ZOJ Monthly, July 2012的相关文章

ZOJ Monthly,Feburary 2012 部分题解

题目链接:点击打开链接 ZOJ 3573 Under Attack 距离做这套题到写题解间隔比较久,题意有些忘了.. #include <iostream> #include <cstdio> #include <algorithm> #include <string> #include <cmath> #include <cstring> #include <queue> #include <set> #in

ZOJ Monthly, November 2012

A.ZOJ 3666 Alice and Bob 组合博弈,SG函数应用 #include<vector> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn = 10000 + 100; int SG[maxn]; vector<int> g[maxn]; int mex(int u) { //minimal exc

Help Bob (141 - ZOJ Monthly, July 2015 )

题意: 现在有1-n个数,每次从中取出一个数,同时把这个数的所有因子取出.最后一个不能取数的人输 分析: 1是所有数的因子,所有第一次任取一个数1都会被取走:下面分两种情况: 我们先把1拿出来,对于其他的数 1.如果先手必败,那么先手第一次取1,然后把这种必败的状态留给对手,则先手必胜 2.如果先手必胜,则按照必胜的策越即可(1作为附带的被取出) #include <cstdio> int main() { int n; while(scanf("%d",&n)==

ZOJ Monthly, August 2012 题解

A: 题目链接:点击打开链接 Alice's present #include <cstdio> #include <iostream> #include <cstring> #include <string> #include <map> #include <queue> #include <set> #include <algorithm> using namespace std; int n, m; in

Twelves Monkeys (multiset解法 141 - ZOJ Monthly, July 2015 - H)

Twelves Monkeys Time Limit: 5 Seconds      Memory Limit: 32768 KB James Cole is a convicted criminal living beneath a post-apocalyptic Philadelphia. Many years ago, the Earth's surface had been contaminated by a virus so deadly that it forced the sur

ZOJ Monthly, February 2012 C,D,F,H

C:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3573 瞎搞题,可以用线段树+lazy过.CB曾经出过一个类似的,可以0(N)的处理.左边加右边减,边走边算. #include <algorithm> #include <iostream> #include <stdlib.h> #include <string.h> #include <iomanip> #incl

The Exchange of Items (141 - ZOJ Monthly, July 2015 - E 最小费用最大流)

The Exchange of Items Time Limit: 2 Seconds      Memory Limit: 65536 KB Bob lives in an ancient village, where transactions are done by one item exchange with another. Bob is very clever and he knows what items will become more valuable later on. So,

ZOJ 3436 July Number(DFS)

题意   把一个数替换为这个数相邻数字差组成的数  知道这个数只剩一位数  若最后的一位数是7  则称原来的数为 July Number  给你一个区间  求这个区间中July Number的个数 从7开始DFS  位数多的数总能由位数小的数推出 #include <bits/stdc++.h> using namespace std; const int N = 1e6; int july[N], n; set<int> ans; int pw[] = {1, 10, 100,

135 - ZOJ Monthly, August 2014

135 - ZOJ Monthly, August 2014 A:构造问题,推断序列奇偶性.非常easy发现最小值不是1就是0.最大值不是n就是n - 1,注意细节去构造就可以 E:dp.dp[i][j]表示长度i,末尾状态为j的最大值,然后每一个位置数字取与不取,不断状态转移就可以 G:就一个模拟题没什么好说的 H:dfs,每次dfs下去,把子树宽度保存下来,然后找最大值,假设有多个.就是最大值+cnt宽度 I:构造,假设r * 2 > R,肯定无法构造.剩下的就二分底边.按等腰三角形去构造就