HGOI20180813 (NOIP2018 提高组 Day2 模拟试题)
省常中省选提高Day2 继续
第一题就考了贪心,正解95pts的贪心策略第一印象是想到的,但是被自己否定掉了qwq,然后打了
不是正解的贪心,样例5没过(可怜)思路如下:先找出每个门对应可以通过的人数是多少,每个人能通过多少门逃走
然后枚举能通过门最少的点优先选,合法门里面可以通过这扇门逃走人数最少的一扇门,这样做(57pts)
贴下代码:
# include <bits/stdc++.h>
using namespace std;
const int MAXN=;
struct rec{
int x,y,cnt;
bool flag;
};
rec a[MAXN],b[MAXN];
inline bool cmp(rec a,rec b)
{
if (a.y!=b.y) return a.y<b.y;
else return a.x<b.x;
}
inline bool cmpa(rec a,rec b)
{
if (a.cnt!=b.cnt) return a.cnt>b.cnt;
else if (a.x!=b.x) return a.x<b.x;
else return a.y<b.y;
}
int main()
{
//freopen("guide.in","r",stdin);
// freopen("guide.out","w",stdout);
int num; scanf("%d",&num);
int n; scanf("%d",&n);
for (register int i=;i<=n;i++) {
scanf("%d%d",&a[i].x,&a[i].y);
a[i].x++;a[i].y++;
a[i].cnt=;a[i].flag=true;
}
for (register int i=;i<=n;i++) {
scanf("%d%d",&b[i].x,&b[i].y);
b[i].x++;b[i].y++;
b[i].flag=true;
b[i].cnt=;
}
for (register int i=;i<=n;i++) {
for (register int j=;j<=n;j++)
if (b[j].x>=a[i].x&&b[j].y>=a[i].y) b[j].cnt++,a[i].cnt++;
}
int ans=;
int yy=;
for (register int i=;i<=n;i++) {
int maxx=,minn=INT_MAX,p=-,w;
for (register int j=;j<=n;j++)
if (a[j].flag) {
if (a[j].cnt<=minn) {
minn=a[j].cnt; w=j;
}else if (a[j].cnt==minn&&a[j].x>a[w].x){
minn=a[j].cnt; w=j;
}else if (a[j].cnt==minn&&a[j].x==a[w].x&&a[j].y>a[w].x){
minn=a[j].cnt; w=j;
}
}
a[w].flag=false;
minn=INT_MAX;
for (register int j=;j<=n;j++)
if (b[j].flag&&b[j].x>=a[w].x&&b[j].y>=a[w].x&&b[j].cnt<=minn)
if (b[j].cnt<minn){
p=j; minn=b[j].cnt;
} else if (b[j].x>b[p].x) {
p=j; minn=b[j].cnt;
} else if (b[j].x==b[p].x&&b[j].y>b[p].x) {
p=j; minn=b[j].cnt;
}
if (p==-) continue;
b[p].flag=false; ans++;
for (int j=;j<=n;j++) if (a[j].x<=b[p].x&&a[j].y<=b[p].y) a[j].cnt--;
for (int j=;j<=n;j++) if (b[j].x<=a[w].x&&b[j].y<=a[w].y) b[j].cnt--;
}
printf("%d\n",ans);
return ;
}
正解是什么呢?
其实出奇的简单,从左到右扫每一个门,然后找这个门前面合法的y坐标最小的人。这样可以过95pts。
但是这里开了O2优化所以我们考虑stl优化O(n2)的初始化维护:set
#include<bits/stdc++.h>
using namespace std;
const int MAXN = ;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = ; int f = ;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * + c - '';
x *= f;
}
template <typename T> void write(T x) {
if (x < ) x = -x, putchar('-');
if (x > ) write(x / );
putchar(x % + '');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
set <int> st;
bool type[MAXN]; int pos[MAXN];
int main() {
freopen("guide.in", "r", stdin);
freopen("guide.out", "w", stdout);
int num; read(num);
int n; read(n);
for (int i = ; i <= n; i++) {
int x, y; read(x), read(y);
type[x] = true, pos[x] = y;//type[x]表示x坐标的这个位置是人(true)还是人(false)
}
for (int i = ; i <= n; i++) {
int x, y; read(x), read(y);
type[x] = false, pos[x] = y;
}
int ans = ;
for (int i = ; i < * n; i++) {
if (type[i]) st.insert(pos[i]);//如果x坐标这个位置是人,那么塞入set(待门来找)
else {
set <int> :: iterator tmp = st.lower_bound(pos[i]);//tmp指向这个门前面下面离这个门最近的人之后的那个位置
if (tmp == st.begin()) continue;//如果没找到就continue
tmp--; ans++; //累加答案
st.erase(tmp);//注意tmp是指这个门前面下面离这个门最近的人之后的那个位置而不是人的位置刚好比人的位置多1,所以tmp--才是指向那个人
}
}
writeln(ans);
return ;
}
看数据范围这道题是dfs,需要注意这样一个性质:
不管怎么换行中的数字是不会发生除了顺序的变化的,那么我们就暴力找出每一次交换,看看个数是否相同,如果相同就是合法
如果不同就是不合法就不对应的,这是可行性剪枝。(首先你要保证每行的个数一样,才能最终判断)
既然暴力判断交换那么就不用记录状态了,直接回溯暴力判断是否是中心对称即可
#include<bits/stdc++.h>
using namespace std;
const int MAXN = ;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = ; int f = ;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * + c - '';
x *= f;
}
template <typename T> void write(T x) {
if (x < ) x = -x, putchar('-');
if (x > ) write(x / );
putchar(x % + '');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
int n, m;
char mp[MAXN][MAXN];
int rnum[MAXN], cnum[MAXN];
bool visrow[MAXN], viscol[MAXN], solved;
bool row[MAXN][MAXN], col[MAXN][MAXN];
void check() {
for (int i = ; i <= n; i++)
for (int j = ; j <= m; j++)
if (mp[rnum[i]][cnum[j]] != mp[rnum[n - i + ]][cnum[m - j + ]]) return;
printf("YES\n");
solved = true;
}
void workc(int pos, int type) {
if (solved) return;
if (pos == ) {
check();
return;
}
if (type) {
for (int i = ; i <= m; i++) {
viscol[i] = true;
cnum[pos] = i;
workc(pos - , );
viscol[i] = false;
}
} else {
for (int i = ; i <= m; i++) {
if (viscol[i]) continue;
for (int j = i + ; j <= m; j++)
if (!viscol[j] && col[i][j]) {
viscol[i] = true;
viscol[j] = true;
cnum[pos] = i;
cnum[m + - pos] = j;
workc(pos - , );
viscol[i] = false;
viscol[j] = false;
}
return;
}
}
}
void workr(int pos, int type) {
if (solved) return;
if (pos == ) {
workc((m + ) / , m & );
return;
}
if (type) {
for (int i = ; i <= n; i++) {
visrow[i] = true;
rnum[pos] = i;
workr(pos - , );
visrow[i] = false;
}
} else {
for (int i = ; i <= n; i++) {
if (visrow[i]) continue;
for (int j = i + ; j <= n; j++)
if (!visrow[j] && row[i][j]) {
visrow[i] = true;
visrow[j] = true;
rnum[pos] = i;
rnum[n + - pos] = j;
workr(pos - , );
visrow[i] = false;
visrow[j] = false;
}
return;
}
}
}
int main() {
freopen("fragment.in", "r", stdin);
freopen("fragment.out", "w", stdout);
int T, num; read(num), read(T);
while (T--) {
read(n), read(m);
for (int i = ; i <= n; i++)
scanf("%s", mp[i] + );
for (int i = ; i <= n; i++)
for (int j = ; j <= n; j++) {
static char x[MAXN], y[MAXN];
for (int k = ; k <= m; k++) {
x[k] = mp[i][k];
y[k] = mp[j][k];
}
sort(x + , x + m + );
sort(y + , y + m + );
row[i][j] = true;
for (int k = ; k <= m; k++)
if (x[k] != y[k]) row[i][j] = false;
}
for (int i = ; i <= m; i++)
for (int j = ; j <= m; j++) {
static char x[MAXN], y[MAXN];
for (int k = ; k <= n; k++) {
x[k] = mp[k][i];
y[k] = mp[k][j];
}
sort(x + , x + n + );
sort(y + , y + n + );
col[i][j] = true;
for (int k = ; k <= n; k++)
if (x[k] != y[k]) col[i][j] = false;
}
solved = false;
workr((n + ) / , n & );
if (!solved) printf("NO\n");
}
return ;
}
由于在K天之后必须所有的旅者都要回到他的家乡所以这n个人的初始序列按照题意模拟必须要是多个环,
且这些环的长度必须是k的质因数,那么这个问题就转变为对K进行质因数分解求出的所有质因数的多重集求和恰为n是否合法
我们现在考虑这个问题,
显然输入的K可能会有重复所以我们并不用每次对K都分解质因数,所以我们用memk[i]记录当前都多少不同种类的K ,q为当前数目
设当前是在memk数组中的第pos个K是当前的K那么p[pos][ ]为mem[pos]的质因数,个数记为cnt[pos]
讲了一大坨就是这个程序:
read(n), read(k);//读入n k
int pos = ; //找之前算过的k
for (int i = ; i <= q; i++)
if (memk[i] == k) pos = i; //找K
if (pos == ) {//没找到qwq
pos = ++q;
memk[q] = k;
long long tmp = k; //分解质因数tmp=k
for (int i = ; 1ll * prime[i] * prime[i] <= tmp; i++)
if (tmp % prime[i] == ) {
p[pos][++cnt[pos]] = prime[i];
while (tmp % prime[i] == ) tmp /= prime[i];
}
if (tmp != ) p[pos][++cnt[pos]] = tmp;//分解质因数
我们来讨论几种简单的情况
cnt[pos]=1那么如果这个仅存的质因数被n整除那么久不行,否则行
bool flg = false;
for (int i = ; i <= cnt[pos]; i++)//依次检测
if (n % p[pos][i] == ) {//行,那么就是true
printf("YES\n");
flg = true;
break;
}
if (flg) continue;
if (cnt[pos] <= ) {//否则就是false
printf("NO\n");
continue;
}
cnt[pos]=2的情况就是ax+by=n求是否存在正数解(a,b)那么就是ex_gcd
这里是ex_gcd的板子(求ax+by=gcd(a,b)求(x,y)的值)
void exgcd(long long a, long long b, long long &x, long long &y) {
if (b == ) {
x = ;
y = ;
return;
}
long long q = a / b, r = a % b;
exgcd(b, r, y, x);
y -= q * x;
}
考虑这样一个问题 已知xp1+yp2=gcd(p1,p2)=1求xp1+yp2=n的解
令x‘p1+y'p2=n成立,xp1+yp2=1成立
那么就有n(xp1+yp2)=n 所以nxp1+nyp2=n;
所以x'=nx,y'=ny,最小化y'(但是保证大于0)才可能让x’尽可能大直到x>0;
根据定理可知y‘最小为ny%p1,
证明的话就是那 一直从y中拿p1到不能拿为止这个时候就是y-[y/p1]*p1就是就是y%p1,加个n就是ny%p1
所以这里的p1放在全局看就是第一个质因数,p2就是第二个质因数
程序的话就是这样:
if (cnt[pos] == ) {
long long x = , y = ;
exgcd(p[pos][], p[pos][], x, y);
y = (y % p[pos][] + p[pos][]) % p[pos][];//求出最小的y
long long tmp = y * (n % p[pos][]) % p[pos][] * p[pos][]; //求出最大的x
if (tmp <= n) printf("YES\n"); //若最大的n不大于n就是true
else printf("NO\n");//否则是false
continue;
}
cnt[pos]>3怎办?
我们设dist[pos][i]表示第pos个数用所有除第一个质因子外乱加求和(带自定义权求和)得到的sum值%i最小是多少
我们考虑这样一个问题
n=xp1+y(x是p1的系数y是乱加求和sum和)显然要想n分解成立那么y必须要不大于n%p1,解释的话就是如果多了那么不用加y直接进加x就行了
这样我们可以用一个形如堆优化dijkstra算法就行求出最小的sum%i的值
if (cnt[pos] >= ) {
for (int i = ; i < p[pos][]; i++)
dist[pos][i] = INF;
static priority_queue <info> Heap;
dist[pos][] = ; Heap.push((info) {, });
//info有两个值dist和home,dist表示乱加数值的和,home表示%i后的余数
static bool vis[MAXN];
memset(vis, false, sizeof(vis));
while (!Heap.empty()) {
while (!Heap.empty() && vis[Heap.top().home]) Heap.pop();
if (Heap.empty()) break;
info tmp = Heap.top(); Heap.pop();
for (int i = ; i <= cnt[pos]; i++) {
int dest = (tmp.home + p[pos][i]) % p[pos][]; //乱加
if (dist[pos][dest] > tmp.dist + p[pos][i]) {
dist[pos][dest] = tmp.dist + p[pos][i];
Heap.push((info) {dist[pos][dest], dest});
}
}
}
}
}
判断的话就是这样:
int tmp = n % p[pos][];//就是y
if (dist[pos][tmp] <= n) printf("YES\n");//有乱加的sum%y<=n就一定可以
else printf("NO\n");
那么std如下:
#include<bits/stdc++.h>
using namespace std;
const int MAXQ = ;
const int MAXLOG = ;
const int MAXN = ;
const int MAXP = 2e6 + ;
const int MAXV = 3.2e7 + ;
const long long INF = 4e18;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = ; int f = ;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * + c - '';
x *= f;
}
template <typename T> void write(T x) {
if (x < ) x = -x, putchar('-');
if (x > ) write(x / );
putchar(x % + '');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
int q; long long memk[MAXQ];
int tot, prime[MAXP], f[MAXV];
int cnt[MAXQ]; long long p[MAXQ][MAXLOG];
long long dist[MAXQ][MAXN];
struct info {long long dist; int home; };
bool operator < (info a, info b) {
return a.dist > b.dist;
}
void exgcd(long long a, long long b, long long &x, long long &y) {
if (b == ) {
x = ;
y = ;
return;
}
long long q = a / b, r = a % b;
exgcd(b, r, y, x);
y -= q * x;
}
int main() {
freopen("fantasy.in", "r", stdin);
freopen("fantasy.out", "w", stdout);
int num; read(num);
for (int i = ; i < MAXV; i++) {
if (f[i] == ) prime[++tot] = f[i] = i;
for (int j = ; j <= tot && prime[j] <= f[i]; j++) {
int tmp = prime[j] * i;
if (tmp >= MAXV) break;
f[tmp] = prime[j];
}
}
int T; read(T);
while (T--) {
long long n, k;
read(n), read(k);
int pos = ;
for (int i = ; i <= q; i++)
if (memk[i] == k) pos = i;
if (pos == ) {
pos = ++q;
memk[q] = k;
long long tmp = k;
for (int i = ; 1ll * prime[i] * prime[i] <= tmp; i++)
if (tmp % prime[i] == ) {
p[pos][++cnt[pos]] = prime[i];
while (tmp % prime[i] == ) tmp /= prime[i];
}
if (tmp != ) p[pos][++cnt[pos]] = tmp;
if (cnt[pos] >= ) {
for (int i = ; i < p[pos][]; i++)
dist[pos][i] = INF;
static priority_queue <info> Heap;
dist[pos][] = ; Heap.push((info) {, });
static bool vis[MAXN];
memset(vis, false, sizeof(vis));
while (!Heap.empty()) {
while (!Heap.empty() && vis[Heap.top().home]) Heap.pop();
if (Heap.empty()) break;
info tmp = Heap.top(); Heap.pop();
for (int i = ; i <= cnt[pos]; i++) {
int dest = (tmp.home + p[pos][i]) % p[pos][];
if (dist[pos][dest] > tmp.dist + p[pos][i]) {
dist[pos][dest] = tmp.dist + p[pos][i];
Heap.push((info) {dist[pos][dest], dest});
}
}
}
}
}
bool flg = false;
for (int i = ; i <= cnt[pos]; i++)
if (n % p[pos][i] == ) {
printf("YES\n");
flg = true;
break;
}
if (flg) continue;
if (cnt[pos] <= ) {
printf("NO\n");
continue;
}
if (cnt[pos] == ) {
long long x = , y = ;
exgcd(p[pos][], p[pos][], x, y);
y = (y % p[pos][] + p[pos][]) % p[pos][];
long long tmp = y * (n % p[pos][]) % p[pos][] * p[pos][];
if (tmp <= n) printf("YES\n");
else printf("NO\n");
continue;
}
int tmp = n % p[pos][];
if (dist[pos][tmp] <= n) printf("YES\n");
else printf("NO\n");
}
return ;
}
HGOI20180813 (NOIP2018 提高组 Day2 模拟试题)的更多相关文章
- HGOI20180812 (NOIP2018 提高组 Day1 模拟试题)
前缀数组其实就是有序的,那么答案显然是 我们尝试求出通项公式: 证明如下: 因为 所以: 解之得: 更加通俗的写法如下: 易知 令 那么, (错位相减) 由易知等式代入得, 所以, 所以程 ...
- NOIP2018提高组Day2 解题报告
前言 关于\(NOIP2018\),详见此博客:NOIP2018学军中学游记(11.09~11.11). \(Day2\)的题目和\(Day1\)比起来,真的是难了很多啊. \(T1\):旅行(点此看 ...
- 冲刺NOIP2015提高组复赛模拟试题(五)2.道路修建
2.道路修建 描述 Description liouzhou_101最悲痛的回忆就是NOI2011的道路修建,当时开了系统堆栈,结果无限RE… 出于某种报复心理,就把那题神奇了一下: 在 Z星球上有N ...
- CCF-NOIP-2018 提高组(复赛) 模拟试题(四)
T1 贪吃蛇 [问题描述] 贪吃蛇是一个好玩的游戏.在本题中,你需要对这个游戏进行模拟. 这个游戏在一个 \(n\) 行 \(m\) 列的二维棋盘上进行. 我们用 \((x, y)\) 来表示第 \( ...
- CCF-NOIP-2018 提高组(复赛) 模拟试题(九)(2018 CSYZ长沙一中)
T1 Circle [问题描述] 小 w 的男朋友送给小 w 一个 n 个点 m 条边的图,并且刁难小 w 要她找出点数最少的正环. 小 w 不会做,于是向你求助. [输入格式] 第一行两个整数\(n ...
- CCF-NOIP-2018 提高组(复赛) 模拟试题(七)
T1 Adjoin [问题描述] 定义一种合法的\(0-1\)串:串中任何一个数字都与\(1\)相邻.例如长度为$ 3 的 0-1 $串中,\(101\)是非法的,因为两边的\(1\)没有相邻的\(1 ...
- CCF-NOIP-2018 提高组(复赛) 模拟试题(一)
T1 帽子戏法 问题描述 小 Y 有一个\(n*n*n\)的"帽子立方体" ,即一个\(n\)层的立方体,每层的帽子都 可以排成\(n*n\)的矩阵. "帽子立方体&qu ...
- 破译情报-NOIP2016提高组复赛模拟试题
[题目描述] 最近国安人员截获了一份 RB 国的秘密情报, 全文都是经过加密的,每个单 词都很长.破译人员想到先把单词化简一下,方法是把每个单词尽量取短些的前 缀,但所取的前缀不能是其他单词的前缀. ...
- 冲刺NOIP2015提高组复赛模拟试题(五) 3.破坏基地
3.破坏基地 描述 Description 在Z国和W国之间一直战火不断. 好不容易,W国的间谍把完整的Z国的军事基地的地图到手了. 于是W国决定再次出击,一举击破Z国的防线. W国认真研究了Z国的地 ...
随机推荐
- 剖析管理所有大数据组件的可视化利器:Hue
日常的大数据使用都是在服务器命令行中进行的,可视化功能仅仅依靠各个组件自带的web界面来实现,不同组件对应不同的端口号,如:HDFS(50070),Yarn(8088),Hbase(16010)等等, ...
- kettle学习笔记(九)——子转换、集群与变量
一.概述 kettle中3个重要的步骤: 子转换/映射 在转换里调用一个子转换,便于封装和重用. 集群 集群模式 变量和参数 变量和参数的用法 二.子转换 1.定义子转换 主要由映射输入与映射输出定义 ...
- WPF编程,通过DoubleAnimation控制图片的透明度,将重叠的图片依次显示。
原文:WPF编程,通过DoubleAnimation控制图片的透明度,将重叠的图片依次显示. 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307 ...
- JAVAWEB 项目注册登录模块问题总结
tomcat: 假如tomcat服务器启动出现错误,那就可能是servlet或代码的原因 tomcat服务器出现不能访问页面的情况,可以在eclipse tomcat服务器设置里设置为共享服务器模式 ...
- [Luogu5048] [Ynoi2019模拟赛]Yuno loves sqrt technology III[分块]
题意 长为 \(n\) 的序列,询问区间众数,强制在线. \(n\leq 5\times 10^5\). 分析 考虑分块,暴力统计出整块到整块之间的众数次数. 然后答案还可能出现在两边的两个独立的块中 ...
- 设计模式 笔记 桥接模式 Bridge
//---------------------------15/04/15---------------------------- //Bridge 桥接模式----对象结构型模式 /* 1:意图:将 ...
- C#_IO操作
1.创建文件夹 //using System.IO; Directory.CreateDirectory(%%1); 2.创建文件 //using System.IO; File.Create(% ...
- HTML5 标签实例
html 5 学习1.<p></p> #段落元素定义2.<h1></h1> #标题 h1代表大号的字体.依此变小3.<br /> #实例 代 ...
- 总结com组件问题,随笔记录
一.从 IClassFactory 为 CLSID 为 {00024500-0000-0000-C000-000000000046} 的 COM 组件创建实例失败,原因是出现以下错误:80010001 ...
- 无前趋的顶点优先的拓扑排序方法(JAVA)(转载http://128kj.iteye.com/blog/1706968)
无前趋的顶点优先的拓扑排序方法 该方法的每一步总是输出当前无前趋(即人度为零)的顶点,其抽象算法可描述为: NonPreFirstTopSort(G){//优先输出无前趋的顶点 w ...