T1

解题思路

这题应该不是很难,主要是题意理解问题。

注意给出的两个数组里映射关系已经对应好了,只要判断是否为双射即可

参考程序

#include <bits/stdc++.h>
using namespace std; class RelationClassifier {
public:
string isBijection( vector <int> domain, vector <int> range );
};
int reflect[110], used[110];
string RelationClassifier::isBijection(vector <int> domain, vector <int> range) {
int n = domain.size();
memset(reflect, 255, sizeof(reflect));
memset(used, 255, sizeof(used));
for(int i = 0; i < n; i++){
if(reflect[domain[i]] != -1 && reflect[domain[i]] != range[i]) return "Not";
if(used[range[i]] != -1 && reflect[domain[i]] != range[i]) return "Not";
reflect[domain[i]] = range[i];
used[range[i]] = 1;
}
return "Bijection";
}

T2

解题思路

首先理解题目中的两个操作的意思。

将点都向同一个方向移动相等距离,就是人可以在坐标系里随意走;可以将所有点旋转一个角度,就是人可以任意转动。那么我们只要找出一组互相垂直的直线,使它们穿过尽量多的点。

由于n的范围只有50,我们考虑最为暴力的做法。

首先枚举两个相异点 AB,确定一条直线,再取异于AB的点C,确定另一条垂线,最后扫一遍所有点就好了。

注意这里的特殊情况:AB横或纵坐标相等时特判(斜率的做法,同时还要注意精度);n小于等于3的时候答案就是n

据说有大佬会此题不用浮点运算的做法,欢迎评论指出。

参考程序

#include <bits/stdc++.h>
using namespace std; class PlaneGame {
public:
int bestShot( vector <int> x, vector <int> y );
};
long double k1, b1, k2, b2;
int t, ans;
int PlaneGame::bestShot(vector <int> x, vector <int> y) {
int n = x.size();
if(n <= 2) return n;//特判
ans = 0;
for(int i = 0; i < n - 1; i++)
for(int j = i + 1; j < n; j++)
for(int k = 0; k < n; k++){
if(i == k || j == k) continue;
if(x[i] != x[j] && y[i] != y[j]){
k1 = (long double)(y[j] - y[i]) / (long double)(x[j] - x[i]);
b1 = (long double)y[i] - k1 * (long double)x[i];
k2 = -1.0/k1;
b2 = (long double)y[k] - k2 * (long double)x[k];//两条直线的信息
t = 0;
for(int l = 0; l < n; l++)
if(abs((long double)y[l] - (k1 * x[l] + b1)) <= 0.0001 || abs((long double)y[l] - (k2 * x[l] + b2)) <= 0.0001) t++;//注意精度误差
ans = max(ans, t);
continue;
}
if(y[i] == y[j]){//特判
t = 0;
for(int l = 0; l < n; l++)
if(y[l] == y[i] || x[l] == x[k]) t++;
ans = max(ans, t);
continue;
}
if(x[i] == x[j]){
t = 0;
for(int l = 0; l < n; l++)
if(x[l] == x[i] || y[l] == y[k]) t++;
ans = max(ans, t);
continue;
}
}
return ans;
}

T3

解题思路

首先我们简化问题。如果祖先关系形成一棵树,那么问题就变成了十分简单的树形DP,相信大家都会做。

那么我们看看这道题的麻烦之处。这里有至多15个点有两个父亲。所以再按照原先的做法,有两个父亲的节点的子树就会被计算两次。

如何解决这个问题?最朴素的想法,可能就是切掉和其中一个父亲的关系,使它变为一棵树。但是问题又来了,这样无法维护和那个父亲间的关系(如果那个父亲不选,那么这个点就必须选啊qwq)。所以,这时候15这个数就派上用场了。由于它比较小,所以我们直接暴力枚举有两个父亲的节点,规定它们必须选,或者必须不选。如果我们认定这个节点必须不选,那么它的两个父亲就必须选了。

解决了?不,还有一个问题。仔细思考一下,发现删边也不能乱删。我们必须删指向深度较小的父亲的那条边。

至此,问题解决。最坏时间复杂度 \(O(2^{15}*100)\) 然而由于十分暴力,最慢的点跑了1.2秒(然而时限是两秒qwq)。如果有大佬有更好的做法,可以在品论区提出。

参考程序

程序中通过按最大深度排序及打标记实现删边。

#include <bits/stdc++.h>
using namespace std; class VampireTreeDiv2 {
public:
int countMinSamples( vector <int> A, vector <int> B );
};
const long long mod = 1000000007;
long long n, cnt;
long long lp, f[1010], lin[2010], nxt[2010], deep[1010];
inline void add(long long x, long long y) { lin[++lp] = y; nxt[lp] = f[x]; f[x] = lp; return; }
void get_deep(long long pos, long long father){
deep[pos] = deep[father] + 1;
for(long long t = f[pos]; t; t = nxt[t])
if(deep[lin[t]] < deep[pos] + 1 && lin[t] != father) get_deep(lin[t], pos);
return;
}
struct Node{
long long deep, pos;
Node(long long deep_ = 0, long long pos_ = 0) { deep = deep_; pos = pos_; return; }
};
Node aa[1010];
bool cmp(Node x, Node y){
return x.deep > y.deep;
}
long long rec[1010], recc[1010];
long long caculated[1010];//标记
long long dp[1010][2], c[1010][2];//dp[i][0]表示i不选,dp[i][1]表示i选,c数组维护方案数
long long t;
long long ans, minnum;
int VampireTreeDiv2::countMinSamples(vector <int> A, vector <int> B) {
n = A.size();
cnt = 0;
memset(f, 0, sizeof(f));
lp = 0;
for(long long i = 0; i < n; i++){
add(A[i], i + 1);
if(B[i] != -1){
add(B[i], i + 1);
rec[++cnt] = i + 1;
}
}
ans = 0; minnum = 100010;
memset(deep, 0, sizeof(deep));
get_deep(0, 0);
for(long long i = 0; i <= n; i++) aa[i] = Node(deep[i], i);
sort(aa, aa + n + 1, cmp);//按最大深度排序
for(long long i = 0; i < (1 << cnt); i++){
memset(recc, 0, sizeof(recc));
memset(caculated, 0, sizeof(caculated));
for(long long j = 1; j <= cnt; j++)
if((i >> (j - 1)) & 1)
recc[rec[j]] = 1; else recc[rec[j]] = 2;
for(long long j = 0; j <= n; j++){
t = aa[j].pos;
dp[t][0] = 0; dp[t][1] = 1;
c[t][0] = 1; c[t][1] = 1;
for(long long k = f[t]; k; k = nxt[k]){
if(caculated[lin[k]]){
if(dp[lin[k]][1] > 100000){
dp[t][0] = 100010;
c[t][0] = 0;
}
continue;
}
if(recc[lin[k]] != 0) caculated[lin[k]] = 1;
dp[t][0] += dp[lin[k]][1];
c[t][0] = (c[t][0] * c[lin[k]][1]) % mod;
if(dp[lin[k]][0] < dp[lin[k]][1]){
dp[t][1] += dp[lin[k]][0];
c[t][1] = (c[t][1] * c[lin[k]][0]) % mod;
}
if(dp[lin[k]][0] > dp[lin[k]][1]){
dp[t][1] += dp[lin[k]][1];
c[t][1] = (c[t][1] * c[lin[k]][1]) % mod;
}
if(dp[lin[k]][0] == dp[lin[k]][1]){
dp[t][1] += dp[lin[k]][1];
c[t][1] = (c[t][1] * ((c[lin[k]][0] + c[lin[k]][1]) % mod)) % mod;
}
}
if(recc[t] == 1) dp[t][0] = 100010, c[t][0] = 0;
if(recc[t] == 2) dp[t][1] = 100010, c[t][1] = 0;
}
if(dp[0][0] < minnum){
minnum = dp[0][0];
ans = c[0][0];
} else
if(dp[0][0] == minnum) ans = (ans + c[0][0]) % mod;
if(dp[0][1] < minnum){
minnum = dp[0][1];
ans = c[0][1];
} else
if(dp[0][1] == minnum) ans = (ans + c[0][1]) % mod;
}
return ans;
}

Topcoder SRM 674 Div.2题解的更多相关文章

  1. TopCoder SRM 667 Div.2题解

    概览: T1 枚举 T2 状压DP T3 DP TopCoder SRM 667 Div.2 T1 解题思路 由于数据范围很小,所以直接枚举所有点,判断是否可行.时间复杂度O(δX × δY),空间复 ...

  2. TopCoder SRM 560 Div 1 - Problem 1000 BoundedOptimization & Codeforces 839 E

    传送门:https://284914869.github.io/AEoj/560.html 题目简述: 定义"项"为两个不同变量相乘. 求一个由多个不同"项"相 ...

  3. [topcoder]SRM 633 DIV 2

    第一题,http://community.topcoder.com/stat?c=problem_statement&pm=13462&rd=16076 模拟就可以了. #includ ...

  4. TopCoder SRM 596 DIV 1 250

    body { font-family: Monospaced; font-size: 12pt } pre { font-family: Monospaced; font-size: 12pt } P ...

  5. [topcoder]SRM 646 DIV 2

    第一题:K等于1或者2,非常简单.略.K更多的情况,http://www.cnblogs.com/lautsie/p/4242975.html,值得思考. 第二题:http://www.cnblogs ...

  6. 【topcoder SRM 702 DIV 2 250】TestTaking

    Problem Statement Recently, Alice had to take a test. The test consisted of a sequence of true/false ...

  7. Topcoder SRM 656 (Div.1) 250 RandomPancakeStack - 概率+记忆化搜索

    最近连续三次TC爆零了,,,我的心好痛. 不知怎么想的,这题把题意理解成,第一次选择j,第二次选择i后,只能从1~i-1.i+1~j找,其实还可以从j+1~n中找,只要没有被选中过就行... [题意] ...

  8. Topcoder SRM 648 (div.2)

    第一次做TC全部通过,截图纪念一下. 终于蓝了一次,也是TC上第一次变成蓝名,下次就要做Div.1了,希望div1不要挂零..._(:зゝ∠)_ A. KitayutaMart2 万年不变的水题. # ...

  9. Topcoder SRM 145 DIV 1

    Bonuses 模拟 题意:给你一个序列,按照百分比排序,再将百分比取整,再把剩余百分比加到最大的那几个. 题解:按照题意模拟就好.

随机推荐

  1. PreparedStatement 以及事务的注意事项

    a).PreparedStatement 可以进行批量操作,但是与Statement有一定的区别 1. Statement可以进行不同sql语句的批量操作 即可以同时进行 crud 操作. Strin ...

  2. Navicat 连接数据库避免中文显示乱码问题解决

    在使用Navicat Premium连接数据库进行操作时,为避免出现中文乱码的问题解决: 1.连接SQL Server 在新建数据库时,常规 设置 排序规则 为 Chinese_PRC_CS_AS_W ...

  3. vue cli3项目发布在apache www/vue目录下并配置history路由

    注意:vue项目打包后默认是指向服务器的根路径(比如apache默认www目录是根路径,当然也可以修改),这种情况不需要做路径的配置,只需要做history配置,如果不是发布到根路径而是www/vue ...

  4. Java后端技术面试汇总(第二套)

    1.Java相关 • Arraylist与LinkedList默认空间是多少:• Arraylist与LinkedList区别与各自的优势List 和 Map 区别:• 谈谈HashMap,哈希表解决 ...

  5. 设置队列中文件上的“X”号的点击事件+uploadLimit动态加1

    目的:1.设置文件队列中“x”号的点击事件 2.每次删除服务器文件后,把uploadLimit + 1: 'onUploadSuccess': function (file, data, respon ...

  6. 2018最新版 手机号、验证码正则表达式 jq + 小程序

    HTML: <!-- 表单 --> <input class="weui-input" id="tel" type="tel&quo ...

  7. ASP.NET c# 实验日记(1)

    第一次写有一些紧张,以前学过html,c语言,vb,c#等语言.也自己翻过有关javascript的书,现在的目的是怎么把学习经验写的更具结构化和条理化,大佬勿喷. 在一个集成开发平台里第一步就是新建 ...

  8. iscsi原理

    iscsi原理 一,[名词解释] SCSI:小型计算机系统接口,SCSI作为i输入/输出接口, FC:光纤通道 DAS:直连式存储,指将存储设备通过SCSI接口或光纤通道直接连到一台计算机上. NAS ...

  9. linux版宝塔安装Redis

    1安装服务 2配置设置 3安装PHP扩展 首先,我们来安装服务,进入管理面板--软件管理--运行环境--redis-点击安装,等待完成 完成之后开始第二步,配置设置.这一步根据自己需要进行配置.注意安 ...

  10. mongodb 3.0 WT 引擎性能测试(转载)

    网上转载来的测试,仅供参考.原文地址:http://www.mongoing.com/benchmark_3_0 类机器. 测试均在单机器,单实例的情况下进行. 机器A(cache 12G,即内存&g ...