2019 HZNU Winter Training Day 15 Comprehensive Training
A - True Liars
题意:
那么如果一个人说另一个人是好人,那么如果这个人是好人,说明 对方确实是好人,如果这个是坏人,说明这句话是假的,对方也是坏人。
如果一个人说另一个人是坏人,那么如果这个人是好人,说明对方是坏人,如果这个是坏人,说明 对方是好人。
也就是如果条件是yes说明这两个是相同集合的,否则是两个不同的集合。
思路:
用r[i]表示i结点与根结点的关系,0为相同集合,1为不同集合。这是一个经典的并查集问题。
这样处理之后,还需要判断是否唯一
我们通过并查集,可以将所有人分为若干个集合,其中对于每一个集合,又分为两个集合(好人和坏人,但是不知道哪些是好人,哪些是坏人,我们只有相对关系)
接下来就是从所有大集合中的两个小集合取一个,组成好人集合,判断是否唯一。
背包问题,dp[i][j]表示前i个大集合,好人为j个的方案有多少种,或者dp[i][j]表示当前好人i个,坏人j个的情况有多少种
如果dp[cnt][p1]!=1说明方案不唯一,或者无解。
输出方案就是加个pre数组,从后往前递推呢。
代码:
- /*
- POJ 1417
- */
- #include <stdio.h>
- #include <algorithm>
- #include <iostream>
- #include <string.h>
- #include <vector>
- #include <string>
- using namespace std;
- const int MAXN=;
- int F[MAXN];
- int val[MAXN];
- int find(int x)
- {
- if(F[x]==-)return x;
- int tmp=find(F[x]);
- val[x]+=val[F[x]];
- val[x]%=;
- return F[x]=tmp;
- }
- int a[MAXN][];//a[i][0],a[i][1]表示每个大集合分成两部分的个数
- vector<int>b[MAXN][];
- bool used[MAXN];
- int dp[MAXN][MAXN/];
- int pre[MAXN][MAXN/];
- int main()
- {
- int n,p1,p2;
- while(scanf("%d%d%d",&n,&p1,&p2)==)
- {
- if(n== && p1== && p2==)break;
- memset(F,-,sizeof(F));
- memset(val,,sizeof(val));
- int u,v;
- char str[];
- while(n--)
- {
- scanf("%d%d%s",&u,&v,&str);
- int tmp;
- if(str[]=='y')//相同
- tmp=;
- else tmp=;//相反
- int t1=find(u),t2=find(v);
- if(t1!=t2)
- {
- F[t1]=t2;
- val[t1]=(val[v]-val[u]+tmp+)%;
- }
- }
- for(int i=;i<MAXN;i++)
- {
- b[i][].clear();
- b[i][].clear();
- a[i][]=;
- a[i][]=;
- }
- memset(used,false,sizeof(used));
- int cnt=;
- for(int i=;i<=p1+p2;i++)
- if(!used[i])
- {
- int tmp=find(i);
- for(int j=i;j<=p1+p2;j++)
- {
- if(find(j)==tmp)
- {
- used[j]=true;
- b[cnt][val[j]].push_back(j);
- a[cnt][val[j]]++;
- }
- }
- cnt++;
- }
- memset(dp,,sizeof(dp));
- dp[][]=;
- for(int i=;i<cnt;i++)
- {
- for(int j=p1;j>=;j--)
- {
- if(j-a[i][]>= && dp[i-][j-a[i][]])
- {
- dp[i][j]+=dp[i-][j-a[i][]];
- pre[i][j]=j-a[i][];
- }
- if(j-a[i][]>= && dp[i-][j-a[i][]])
- {
- dp[i][j]+=dp[i-][j-a[i][]];
- pre[i][j]=j-a[i][];
- }
- }
- }
- if(dp[cnt-][p1]!=)
- {
- printf("no\n");
- }
- else
- {
- vector<int>ans;
- ans.clear();
- int t=p1;
- //printf("%d\n",cnt);
- for(int i=cnt-;i>=;i--)
- {
- int tmp=t-pre[i][t];
- //printf("%d\n",i);
- //printf("%d %d\n",t,tmp);
- if(tmp==a[i][])
- {
- for(int j=;j<a[i][];j++)
- ans.push_back(b[i][][j]);
- }
- else
- {
- for(int j=;j<a[i][];j++)
- ans.push_back(b[i][][j]);
- }
- t=pre[i][t];
- }
- sort(ans.begin(),ans.end());
- for(int i=;i<ans.size();i++)
- printf("%d\n",ans[i]);
- printf("end\n");
- }
- }
- return ;
- }
B - The Shortest Path in Nya Graph
题意:有n个点 , m条无向边 ,每条边都是有权值, 并且每个点属于一个楼层 ,楼层上的点到可以到相邻楼层的任意一点 ,但是要花费 c 。没有点的相邻楼层不能互达。求 1 到 n的最小花费。
思路:图建好了就是裸的最短路了。但是建图有点麻烦,参考了别人的代码之后才明白为什么要这样建图。
把楼层看成一个点,第i层可以看成第n+i个点。楼层与该楼层上的点建边,边权为0,单向;楼层与
相邻楼层建边,边权为C,双向;相邻楼层上的点与该楼层建边,边权为C,单向。
代码:
- #include<cstring>
- #include<iostream>
- #include<algorithm>
- #include<cstdio>
- #include<cstdlib>
- #include<cmath>
- #include<vector>
- #include<queue>
- #define N 200100
- #define ll long long
- using namespace std;
- const int inf=0x3f3f3f3f;
- int n,m,c;
- int lay[N];
- bool hava[N];
- bool vis[N];
- int cnt[N];
- int dist[N];
- struct Edge {
- int v;
- int cost;
- Edge(int _v=,int _c=):v(_v),cost(_c) {}
- bool operator <(const Edge &r)const {
- return cost>r.cost;
- }
- };
- vector<Edge>E[N];
- void addedge(int u,int v,int w) {
- Edge it;
- it.v=v;
- it.cost=w;
- E[u].push_back(it);
- }
- void Dijk(int n,int start) { //点的编号从1开始
- memset(vis,false,sizeof(vis));
- for(int i=; i<=n*; i++)dist[i]=inf;
- priority_queue<Edge>que;
- while(!que.empty())que.pop();
- dist[start]=;
- que.push(Edge(start,));
- Edge tmp;
- while(!que.empty()) {
- tmp=que.top();
- que.pop();
- int u=tmp.v;
- if(vis[u])continue;
- vis[u]=true;
- for(int i=; i<E[u].size(); i++) {
- int v=E[tmp.v][i].v;
- int cost=E[u][i].cost;
- if(!vis[v]&&dist[v]>dist[u]+cost) {
- dist[v]=dist[u]+cost;
- que.push(Edge(v,dist[v]));
- }
- }
- }
- }
- int main() {
- // freopen("test.in","r",stdin);
- int t;
- cin>>t;
- int ca=;
- while(t--) {
- for(int i=; i<=n*+; i++)E[i].clear();
- scanf("%d%d%d",&n,&m,&c);
- memset(hava,,sizeof hava);
- for(int i=; i<=n; i++) {
- scanf("%d",&lay[i]);
- hava[lay[i]]=true;
- }
- int u,v,cost;
- for(int i=; i<=m; i++) {
- scanf("%d%d%d",&u,&v,&cost);
- addedge(u,v,cost);
- addedge(v,u,cost);
- }
- if(n<=) {
- printf("Case #%d: 0\n",ca++);
- continue;
- }
- for(int i=; i<n; i++) {
- if(hava[i]&&hava[i+]) {
- addedge(n+i,n+i+,c);
- addedge(n++i,n+i,c);
- }
- }
- for(int i=; i<=n; i++) {
- addedge(lay[i]+n,i,);
- if(lay[i]>)addedge(i,lay[i]-+n,c);
- if(lay[i]<n)addedge(i,lay[i]++n,c);
- }
- Dijk(n,);
- printf("Case #%d: %d\n",ca++,dist[n]>=inf?-:dist[n]);
- }
- return ;
- }
C - The Super Powers
题意:给你一个关于超级幂数的定义,只要一个数是至少两个数的幂,即为超级幂数。例如64=8*8=4*4*4。输出1~2^64-1内所有的超级幂数。
思路:一开始的思想暴力,但是肯定很慢。有两个比较重要的思路:① 找num^x<2^64-1的时候,尝试两边取对数,然后确定x<ln(2^64-1)/ln(num),x在4~ln(2^64-1)/ln(num)里取。② 只有合数的时候才能产生超级幂数,然后判断是否为素数即可。
还有很重要的一点,pow会出错。谨慎使用,wa了很多次,用快速幂才ac。
扔进set里遍历输出即可。
代码:
- /*
- I have a dream!A AC deram!!
- orz orz orz orz orz orz orz orz orz orz orz
- orz orz orz orz orz orz orz orz orz orz orz
- orz orz orz orz orz orz orz orz orz orz orz
- */
- #include<iostream>
- #include<cstdio>
- #include<cstdlib>
- #include<cstring>
- #include<cmath>
- #include<string>
- #include<algorithm>
- #include<vector>
- #include<queue>
- #include<set>
- #include<stack>
- using namespace std;
- typedef long long ll;
- typedef unsigned long long ull;
- const int maxn = 1e4 + ;
- const int N = ;
- #define pa pair<int,int>
- inline int read()
- {
- int x = , f = ; char ch = getchar(); for (; !isdigit(ch); ch = getchar())if (ch == '-')f = -;
- for (; isdigit(ch); ch = getchar())x = x * + ch - ''; return x * f;
- }
- bool isPrime(int n)
- {
- if (n <= )
- return n > ;
- else if (n % == || n % == )
- return false;
- else
- {
- for (int i = ; i * i <= n; i += )
- if (n % i == || n % (i + ) == )
- return false;
- }
- return true;
- }
- ull qsm(int a, int b)
- {
- ull ans = , base = a;
- while (b != ) {
- if (b & != ) {
- ans *= base;
- }
- base *= base;
- b >>= ;
- }
- return ans;
- }
- int main()
- {
- set<ull>ans;
- ans.insert();
- ull maxx = ;
- for (int num = ; num < 1e5; num++)
- {
- if (pow(num, ) > maxx) break;
- double m = log(maxx) / log(num);
- for (int i = ; i < m; i++)
- {
- if (isPrime(i)) continue;
- ull p = qsm(num, i);
- ans.insert(p);
- }
- }
- set<ull>::iterator it;
- //int cnt = 0;
- for (it = ans.begin(); it != ans.end(); it++)
- {
- //cnt++;
- //if (cnt == 10) break;
- printf("%llu\n", *it);
- }
- return ;
- }
D - Music Festival
题意:给你n组区间,每个居间都带有权值,让你选取一些区间使得区间不想交并且权值最大(要每组都至少有一个区间被选到)。
思路:n最多十组,我们就可以用二进制来记录当前唱歌的状态,然后离散化区间点(这样我们就可以用连续的点去表示这些区间),这样就有dp【i】【j】,i表示当前的区间点,j表示当前的状态,dp【i1】【j1】=dp【i】【j】+num【k】。每次寻找当前点往右 能更新的最近的一个区间(一切切的一切都是膜拜大佬的博客来的,离散化真是个好东西)
---------------------
作者:liexss
来源:CSDN
原文:https://blog.csdn.net/liexss/article/details/83511980
代码:
- #include<stdio.h>
- #include<cstring>
- #include<cstdlib>
- #include<algorithm>
- #include<map>
- #include<iostream>
- using namespace std;
- struct inst {
- int x, y;
- int sud;
- int id;
- };
- inst ax[];
- int dp[][];
- int bn[];
- int maxn[];
- int tot, cnt;
- map<int, int>q;
- int cmp(inst a, inst b) {
- if (a.x!=b.x)return a.x < b.x;
- else return a.y < b.y;
- }
- int binary_s(int x) {
- int l = ;
- int r = tot-;
- int ans = tot-;
- while (l <= r) {
- int mid = (l + r) / ;
- if (ax[mid].x >= x) {
- ans = mid;
- r = mid - ;
- }
- else l = mid + ;
- }
- if (x > ax[ans].x) ans++;
- return ans;
- }
- int main(void) {
- int n;
- scanf("%d", &n);
- tot = ;
- cnt = ;
- q.clear();
- memset(maxn, 0x8f, sizeof(maxn));
- memset(dp, 0x8f, sizeof(dp));
- for (int i = ; i <= n; i++) {
- int m;
- scanf("%d", &m);
- for (int z = ; z < m; z++) {
- scanf("%d %d %d", &ax[tot].x, &ax[tot].y,&ax[tot].sud);
- ax[tot].id = i-;
- bn[cnt++] = ax[tot].x;
- bn[cnt++] = ax[tot].y;
- tot++;
- }
- }
- sort(bn, bn + cnt);
- cnt = unique(bn,bn + cnt) - bn;
- sort(ax, ax + tot, cmp);
- for (int i = ; i < cnt; i++) {
- q[bn[i]] = i;
- }
- for (int i = ; i < tot; i++) {
- ax[i].x = q[ax[i].x];
- ax[i].y = q[ax[i].y];
- }
- dp[][] = maxn[] = ;
- for (int i = ; i < cnt; i++) {
- for (int j = ; j < ( << n); j++) {
- dp[i][j] = maxn[j] = max(dp[i][j], maxn[j]);
- if (dp[i][j] < ) continue;
- int pos=binary_s(i);
- int pre = -;
- for (int z = pos; z < tot; z++) {
- if (pre == - || pre == ax[z].x) pre = ax[z].x;
- else break;
- int x1 = ax[z].y;
- int jj = j | ( << ax[z].id);
- int sum = dp[i][j] + ax[z].sud;
- dp[x1][jj] = max(dp[x1][jj], sum);
- }
- }
- }
- if (maxn[( << n) - ] >= ) printf("%d\n", maxn[( << n) - ]);
- else printf("-1\n");
- return ;
- }
E - Nature Reserve
题意:在二维坐标上,有动物的栖息地和一条河流,河流在y=0上,即为X轴。现在要建一个圆形的动物保护区,让动物都在里面,同时这个圆要求和X轴相切。因为只能相切,如果出现动物在X轴两侧的情况输出-1,即不能满足情况。
思路:我们二分半径,然后由于固定了与X轴相切,我们对于每一个点,就可以算出这个点在圆上的时候的圆心的极限距离。然后我们就可以求得每一个点的圆心的区间范围。然后所有点的区间范围都相交,那么证明这个半径可行,否则不可行。
代码:如果你的电脑和我的一样辣鸡,代码基本相同样例跑不出来,可能是精度的问题,建议您不要取很大的数字,取小点吧。
- /*
- I have a dream!A AC deram!!
- orz orz orz orz orz orz orz orz orz orz orz
- orz orz orz orz orz orz orz orz orz orz orz
- orz orz orz orz orz orz orz orz orz orz orz
- */
- #include<iostream>
- #include<cstdio>
- #include<cstdlib>
- #include<cstring>
- #include<cmath>
- #include<string>
- #include<algorithm>
- #include<vector>
- #include<queue>
- #include<set>
- #include<stack>
- using namespace std;
- typedef long long ll;
- typedef unsigned long long ull;
- const int maxn = 1e5 + ;
- #define pa pair<int,int>
- inline int read()
- {
- int x = , f = ; char ch = getchar(); for (; !isdigit(ch); ch = getchar())if (ch == '-')f = -;
- for (; isdigit(ch); ch = getchar())x = x * + ch - ''; return x * f;
- }
- double x[maxn], y[maxn];
- int N;
- bool check(long double R)
- {
- long double l = -100000000000000000.0, r = 100000000000000000.0, t;
- for (int i = ; i <= N; i++)
- {
- if ( * R < y[i])
- return ;
- t = sqrt(R*R - (R - y[i])*(R - y[i]));
- if (l < x[i]-t) l = x[i] - t;
- if (r > t+x[i]) r = t + x[i];
- }
- return l < r;
- }
- int main()
- {
- scanf("%d", &N);
- for (int i = ; i <= N; i++)
- scanf("%lf %lf", &x[i], &y[i]);
- for (int i = ; i <= N; i++)
- {
- if (y[i] * y[N] < )
- {
- printf("-1\n");
- return ;
- }
- y[i] = y[i] > ? y[i] : -y[i];
- }
- long double l = , r = 100000000000000000.0, mid;
- for(int i = ;i <= ;i++)
- {
- mid = (l + r) / 2.0;
- if (check(mid))
- r = mid;
- else
- l = mid;
- }
- printf("%.10Lf\n", mid);
- return ;
- }
F - Graph And Its Complement
题意:给出n,a,b,问,是否有这样的一个图,有n个节点,分成a块,现将两两节点之间有边的把边删除,两两节点之间无边的建边,使得形成的图分成b块。如果有,输出YES,并且输出这个图的邻接矩阵,否则输出NO。这题难就难在是否能推出结论以及答案的输出。
思路:
任何一个图,不管分成多少块,经过上述操作后,必然变成一个联通图。
证:有n个节点,分成m块,任取一块,经过上述操作后,所取的这一块内的任意节点必定与其他块的所有节点相连!
这个结论换句话说就是,a和b里面,至少有一个数是1!知道这个结论就简单了,首先排除所有不含1的情况,然后,b==1的情况,我们将图分成a块,直接另前a-1块都只有一个节点,最后1块包含剩下的节点就行了。当然,对于a==1,只要将要输出反过来一下就可以了(注意,这里要讨论一下a和b都为1的情况,当n==2和n==3时,是不行的直接排除,其他情况都是可行的,特殊处理一下a和b都等于1的输出)
---------------------
作者:JIA-YAO
来源:CSDN
原文:https://blog.csdn.net/qq_41874469/article/details/80658410
代码:
- #include "iostream"
- #include "algorithm"
- using namespace std;
- int n,a,b;
- char ch1='',ch2='';
- int main() {
- cin>>n>>a>>b;
- if (a!=&&b!=||a==&&b==&&n<=&&n>=||a>n||b>n) {cout<<"NO"<<endl; return ;}
- cout<<"YES"<<endl;
- if (a==) swap(a,b), swap(ch1,ch2);
- for (int i=;i<n;i++) {
- for (int j=;j<n;j++) {
- cout<<(i==j?'':(i+==j&&j>=a||j+==i&&i>=a?ch1:ch2));
- }
- cout<<endl;
- }
- }
2019 HZNU Winter Training Day 15 Comprehensive Training的更多相关文章
- 2019 HZNU Winter Training Day 14 Comprehensive Training
A - Choosing Capital for Treeland CodeForces - 219D 题意:有一颗单向边的树,要选取一个结点作为首都.要求是这个结点到其它结点,总共需要翻转的路径数量 ...
- 2019 HZNU Winter Training Day 13 Comprehensive Training
A.Jongmah CodeForces-1110D 题意:你在玩一个数字游戏,有一堆写在瓦片上的数字,希望你能组成最多的三元组(三个数字相同,或顺子). 这题用到的方法是动态规划.f[i][j] ...
- 2019 wannafly winter camp day 3
2019 wannafly winter camp day 3 J 操作S等价于将S串取反,然后依次遍历取反后的串,每次加入新字符a,当前的串是T,那么这次操作之后的串就是TaT.这是第一次转化. 涉 ...
- 2019 wannafly winter camp
2019 wannafly winter camp Name Rank Solved A B C D E F G H I J K day1 9 5/11 O O O O O day2 5 3/11 O ...
- 【Android Developers Training】 15. 启动一个Activity
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- (诡异Floyd&自环)MZ Training 2014 #15 E题(POJ 2240)
你们见过这么诡异的FLOYD吗? 先上题. [Description] 货币的汇率存在差异.比如,如果1美元购买0.5英镑,1英镑买10法郎.而1法国法郎买0.21美元.然后,通过转换货币,一个聪明的 ...
- 2019 CCPC-Wannafly Winter Camp Day5(Div2, onsite)
solve 5/11 补题:7/11 A Cactus Draw Code:zz Thinking :zz 题意:要在n*n的网格内画上一棵节点数为n树,使得没有边相交. 很好想的构造题,因为网格有n ...
- 2019 wannafly winter camp day5-8代码库
目录 day5 5H div2 Nested Tree (树形dp) 5F div2 Kropki (状压dp) 5J div1 Special Judge (计算几何) 5I div1 Sortin ...
- 2019 wannafly winter camp day1-4代码库
目录 day1 F div1 爬爬爬山 (最短路) B div2 吃豆豆 (dp) J div2 夺宝奇兵(暴力) J div1 夺宝奇兵 (权值线段树) C div1 拆拆拆数 E div1 流流流 ...
随机推荐
- Android 开发环境之 VMware 虚拟机(android8.1)
VM版本14 在官网下载androidx86的VMDK文件 官方下载地址 (VMDK文件是VMware的专用文件,比iso镜像文件安装要简便许多,内部已经配置好了,只需要按照虚拟机安装普通流程即可) ...
- 【pycharm】Pycharm对 axios语法的支持问题
问题: 解决办法: 1,找到pychar的settings 2,ECMAScript6
- drf初体验
快速开始 安装 pip install djangorestframework 创建django项目 django-admin startproject mydrf 创建APP cd mydrf py ...
- 数字麦克风PDM信号采集与STM32 I2S接口应用(二)
在使用STM32的数字麦克风I2S接口时,计算采样率让人头疼,芯片手册上没有明确的说法,而手册上的计算方法经过测试确和实验不符.借助搜索引擎,大部分资料都是来自于开发板卖家或开发板论坛,主要是咪头采集 ...
- RocketMQ中Broker的刷盘源码分析
上一篇博客的最后简单提了下CommitLog的刷盘 [RocketMQ中Broker的消息存储源码分析] (这篇博客和上一篇有很大的联系) Broker的CommitLog刷盘会启动一个线程,不停地 ...
- redis实现排行榜
1 前言 实现一个排版榜,我们通常想到的就是mysql的order by 简单粗暴就撸出来了.但是这样真的优雅吗? 数据库是系统的瓶颈,这是众所周知的.如果给你一张百万的表,让你排序做排行榜,花费的时 ...
- 如何用Hexo+Github创建自己的技术博客
注册一个github GitHub官网.按照一般的网站注册登录执行就好了,不详细说. 安装git 安装很简单,一直下一步 git安装教程 很多教程里都说要配置环境变量,我本人安装过5次左右的git,一 ...
- Vue系列:为不同页面设置body背景颜色
由于SPA页面的特性,传统的设置 body 背景色的方法并不通用. 解决方案:利用组件内的路由实现 代码参考如下
- ethtool工具使用实例
使用ethtool工具可以查看和修改网卡(NIC卡)设备配置,下面我们来看ethtool的具体用法. 1.显示网卡属性 ethtool命令后直接跟网卡名称,可以显示关于该网卡的属性值: # ethto ...
- Java虚拟机(一)-Java内存区域
通过看深入理解java虚拟机这本书,大致总结一些笔记,或者提出一些问题,希望大家深入交流学习,第一次写博客,大家多多支持 Java虚拟机对于很多Java开发人员每天都在用,但是大部分人初学者对这些并不 ...