前言:2015/5/13用虚拟OJ的方式3人一台电脑打了CCPC女生赛,一共做出9题(共10题),因为自己太坑D

题错了9发,罚时爆炸,在现场就只能排第2,上交小姐姐太强啦,很稳。

接下来就把做出来的题,写个简易题解,题目在HDU上可以找到,题号对应为6023-6032。

A:模拟水题,不说了。

B:题意:有n个节点,我们可以选择在每个节点建或不建商店。 对于第i个点,其坐标是a[i].x,建设商店的成

本为a[i].c。 每个节点对答案的贡献是——dis(i点的坐标, i向左方向走最近节点的坐标)。 让你安排一个方

案,使得”∑所有节点对答案的贡献”尽可能小

解法:简单DP。显然要按照坐标排序。 用dp[i][j]表示我们考虑到第i个点,i向左走,距离i最近的有商店的点

是j对应的前缀最小成本。 那么有移dp[i][j] = dp[i - 1][j] + dis(i, j); 特别的,如果我们在第i个点建服务器,

那么有dp[i][i] = max(dp[i - 1][j], for all j); 然后维护全局最大值即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
typedef long long LL;
LL dp[3005][3005];
pair<LL, LL> p[3005];
int main()
{
int n;
while(~scanf("%d", &n))
{
for(int i = 1; i <= n; i++)
scanf("%lld %lld", &p[i].first, &p[i].second);
sort(p + 1, p + 1 + n);
memset(dp, 0x3F, sizeof(dp));
dp[1][1] = p[1].second;
for(int i = 1; i <= n; i++)
for(int k = 1; k <= i; k++)
{
dp[i + 1][i + 1] = min(dp[i + 1][i + 1], dp[i][k] + p[i + 1].second);
dp[i + 1][k] = min(dp[i + 1][k], dp[i][k] + p[i + 1].first - p[k].first);
}
LL ans = __LONG_LONG_MAX__;
for(int i = 1; i <= n; i++)
ans = min(ans, dp[n][i]);
printf("%lld\n", ans);
}
return 0;
}

C:题意:n个数,恰好去掉一个后的最大gcd

解法:维护一个前缀GCD和后缀GCD,暴力枚举去掉的数是哪个,然后前缀后缀max一下就好了。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
int a[maxn],l[maxn],r[maxn];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
l[1]=a[1];
for(int i=2;i<=n;i++) l[i]=__gcd(a[i],l[i-1]);
r[n]=a[n];
for(int i=n-1;i>=1;i--) r[i]=__gcd(a[i],r[i+1]);
int ans = 1;
for(int i=1;i<=n;i++)
{
int temp;
if(i==1) temp = r[i+1];
else if(i==n) temp= l[i-1];
else temp = __gcd(l[i-1],r[i+1]);
ans=max(ans,temp);
}
printf("%d\n",ans);
}
return 0;
}

D:题意:让你把一个图删成一棵树,使得1到每个点的最短路保持不变

解法:我们可以直接求出1到每个点的最短路,然后看看每个点的前驱边可能是有几条(显然对于每一条合法

前驱边,以任何一条作为前缀都是等价的) 所有点可能的前驱边数量,乘起来就是最后的答案了。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 60;
struct EDGE
{
int to, next, len;
EDGE(int to = 0, int next = 0, int len = 0): to(to), next(next), len(len) {}
} edge[maxn * maxn];
int head[maxn], edgecnt;
const int mod = 1e9 + 7;
void init()
{
memset(head, -1, sizeof(head));
edgecnt = 0;
}
void add(int s, int t, int l)
{
edge[edgecnt] = EDGE(t, head[s], l);
head[s] = edgecnt++;
}
int dis[maxn], cnt[maxn];
bool f[maxn];
int main()
{
int n;
while(~scanf("%d", &n))
{
init();
for(int i = 1; i <= n; i++)
{
char s[maxn];
scanf("%s", s + 1);
for(int j = 1; j <= n; j++)
{
// if(i != j)
// {
if(s[j] != '0') add(i, j, s[j] - 48);
// }

}
}
memset(dis, 0x3F, sizeof(dis));
memset(cnt, 0, sizeof(cnt));
memset(f, 0, sizeof(f));
cnt[1] = 1;
dis[1] = 0;
queue<int>q;
q.push(1);
while(!q.empty())
{
int u = q.front();
q.pop();
f[u] = 0;
for(int i = head[u]; ~i; i = edge[i].next)
{
int v = edge[i].to;
int d = dis[u] + edge[i].len;
if(d <= dis[v])
{
if(d == dis[v])
{
cnt[v]++;
}
else
{
dis[v] = d;
cnt[v] = 1;
if(!f[v])
{
q.push(v);
f[v] = 1;
}
}
}
}
}
int ans = 1;
for(int i = 2; i <= n; i++)
{
if(dis[i] == 0x3F3F3F3F)
ans = 0;
else
ans = (long long)ans * cnt[i] % mod;
}
printf("%d\n", ans);
}
}

E:签到题,不说了。

F:神题, HDU至今只有Claris大神A掉,好像是他出的题?

G:题意:有n个点,每个点要不不连前缀边 要不连所有前缀边,判断是否可以n个数配成n/2对。

解法:贪心,显然我们从后向前看,任意时刻2的数量一定要比1的数量多。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int a[N];
int n;
int f()
{
int t=0;
for(int i=n;i>=2;i--){
if(a[i]==1) t++;
else{
if(t==0) return 0;
t--;
}
}
if(t%2) return 1;
return 0;
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=2;i<=n;i++) scanf("%d",&a[i]);
if(!f()) printf("No\n");
else printf("Yes\n");
}
return 0;
}

H:题意:有一串珍珠,长度为n(1e18) 每个珍珠要不染色成红色,要不染色成蓝色。 要求任何连续素数

长度的珍珠,都必须是红色个数>=蓝色个数 让你求出有多少种对这串珍珠的染色方案。

解法:我们用f[i]表示长度为i的珍珠串的合法染色方案数。 显然,如果第i颗珍珠染色为红色,f[i-1]的都依然

合法、 如果第i颗珍珠染色为蓝色,要求这连续三颗必须为红红蓝,于是收获f[i-3]的贡献。 而只要2、3素数

满足要求,所有素数都会满足要求(因为它们可以拆成2与3)。 所以得到递推式:f[i] = f[i - 1] + f[i - 3] 矩

阵快速幂即可求解。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int mod=1e9+7;
LL n;
struct matrix
{
int A[3][3];
void init()
{
memset(A,0,sizeof(A));
}
void gete()
{
memset(A,0,sizeof(A));
A[0][0]=A[1][1]=A[2][2]=1;
}
};
matrix cheng(matrix a,matrix b)
{
matrix ans;
ans.init();
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
for(int k=0;k<3;k++){
ans.A[i][j]=(ans.A[i][j]+1ll*a.A[i][k]*b.A[k][j]%mod)%mod;
}
}
}
return ans;
}
matrix qpow(matrix x,LL y)
{
matrix ans;
ans.gete();
while(y){
if(y%2) ans=cheng(ans,x);
x=cheng(x,x);
y/=2;
}
return ans;
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%lld",&n);
matrix A;
A.A[0][0]=1,A.A[0][1]=0,A.A[0][2]=1;
A.A[1][0]=1,A.A[1][1]=0,A.A[1][2]=0;
A.A[2][0]=0,A.A[2][1]=1,A.A[2][2]=0;
A=qpow(A,n-2);
int ans=0;
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
ans=(ans+A.A[i][j])%mod;
}
}
printf("%d\n",ans);
}
return 0;
}

I:题意:一棵树,有n(1e5)个节点。 同时存在m(1e5)个询问。每个询问给你2个集合。 要你求出

for(第一个集合中的点x)

{

for(第一个集合中的点y)

{

max(dep[ lca(x,y) ]);

}

}

解法:显然,如果我们考虑了x,哪个点y,会可能产生尽可能大的dep[ lca(x,y) ]呢? 一定是与x时间戳(dfs

序)最接近的节点y。 因为要知道一棵子树的dfs序必然是连续的一段。 所以如果y是x或者是x的子孙,都一样

会收获对于x最好的y,产生的dep[ lca(x,y) ]就等于dep[x] 而如果y不是x的子孙,则还是基于dfs序定理,最接

近的一定可以获取到最大的dep[ lca(x,y) ]

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
struct EDGE
{
int to, next;
EDGE(int to = 0, int next = 0): to(to), next(next) {}
} edge[maxn * 2];
int head[maxn], edgecnt;
int dfscr[maxn * 3];
int Left[maxn], Right[maxn];
int dfsclk;
void init()
{
memset(head, -1, sizeof(head));
edgecnt = 0;
dfsclk = 0;
}
void add(int s, int t)
{
edge[edgecnt] = EDGE(t, head[s]);
head[s] = edgecnt++;
}
void dfs(int u, int fa, int dep)
{
Left[u] = ++dfsclk;
dfscr[dfsclk] = dep;
for(int i = head[u]; ~i; i = edge[i].next)
{
int v = edge[i].to;
if(v == fa)
continue;
dfs(v, u, dep + 1);
dfscr[++dfsclk] = dep;
}
}
int mi[maxn * 3][19];
int que(int l, int r)
{
int k = 0;
while((1 << k + 1) <= r - l + 1)
k++;
return min(mi[l][k], mi[r - (1 << k) + 1][k]);
}
int main()
{
int n, q;
while(~scanf("%d %d", &n, &q))
{
init();
for(int i = 1; i < n; i++)
{
int s, t;
scanf("%d %d", &s, &t);
add(s, t);
add(t, s);
}
dfs(1, 0, 1);
for(int i = 1; i <= dfsclk; i++)
mi[i][0] = dfscr[i];
for(int i = 1; (1 << i) <= dfsclk; i++)
for(int j = 1; j + (1 << i) - 1 <= dfsclk; j++)
mi[j][i] = min(mi[j][i - 1], mi[j + (1 << i - 1)][i - 1]);
while(q--)
{
vector<int> x, y;
int k;
scanf("%d", &k);
while(k--)
{
int t;
scanf("%d", &t);
x.push_back(Left[t]);
}
scanf("%d", &k);
while(k--)
{
int t;
scanf("%d", &t);
y.push_back(Left[t]);
}
sort(x.begin(), x.end());
int ans = 1;
for(auto &i : y)
{
auto it = upper_bound(x.begin(), x.end(), i);
if(it != x.end())
{
ans = max(ans, que(i, *it));
}
if(it != x.begin())
{
it--;
ans = max(ans, que(*it, i));
}
}
printf("%d\n", ans);
}
}
return 0;
}

J:给你n(30)个串,每个串的长度也不超过30。 Alice和Bob博弈,Alice先手,初始是空串。 每次要在串

前或串后任意加一个小写字符,使得原始串中,至少存在一个串包含该子串。不能操作就输了。 双方博弈的

关键字是(胜败,自己分数尽可能高,对手分数尽可能低) 让你输出Alice的最优结局。

解法:直接按照基本SG博弈的方法,从终止态退回初始态(空串)就好啦!但是要 先预处理好所有合法串,再

按长度排序得到DP的顺序。 队友开始没有预处理所有移直接写了暴力转移,T成SB。预处理好之后,按照

博弈DAG做DP就可以了。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 35;
int n;
char dict[maxn][maxn];
vector<int> vec[15000];
int val[15000];
unordered_map<string, int> GetID;
int cnt = 0;
struct status
{
int win;
int me, you;
status(bool win = 0, int me = 0, int you = 0): win(win), me(me), you(you) {}
bool cmp(const status &b)
{
if(win == b.win)
if(me == b.me)
return you < b.you;
else
return me > b.me;
else
return win > b.win;
}
} dp[15000];
int GetVal(string &s)
{
int ret = 0;
int mx = 0;
for(auto &i : s)
{
ret += i - 'a' + 1;
mx = max(mx, i - 'a' + 1);
}
ret *= mx;
const char *c = s.c_str();
for(int i = 1; i <= n; i++)
if(strstr(dict[i], c))
ret++;
return ret;
}

status dfs(int u)
{
if(dp[u].win != -1)
return dp[u];
dp[u] = status(0, 0, 0);
for(auto &v : vec[u])
{
status temp = dfs(v);
temp.win ^= 1;
swap(temp.me, temp.you);
temp.me += val[v];
if(temp.cmp(dp[u]))
dp[u] = temp;
}
return dp[u];
}
void BuildGraph()
{
cnt = 0;
GetID.clear();
GetID[""] = cnt++;
for(int i = 1; i <= n; i++)
for(int st = 0; dict[i][st]; st++)
for(int en = st; dict[i][en]; en++)
{
string s(dict[i] + st, dict[i] + en + 1);
if(!GetID.count(s))
{
GetID[s] = cnt++;
val[cnt - 1] = GetVal(s);
}
}
for(int i = 0; i < cnt; i++)
{
dp[i].win = -1;
vec[i].clear();
}
for(auto &i : GetID)
{
const string &en = i.first;
if(en.size() < 1)
continue;
string st = string(en.begin(), en.end() - 1);
vec[GetID[st]].push_back(i.second);
st = string(en.begin() + 1, en.end());
vec[GetID[st]].push_back(i.second);
}
}
int main()
{
while(~scanf("%d", &n))
{
for(int i = 1; i <= n; i++)
scanf("%s", dict[i]);
BuildGraph();
status ans = dfs(0);
if(ans.win)
puts("Alice");
else
puts("Bob");
printf("%d %d\n", ans.me, ans.you);
}
return 0;
}