NOIP2017提高组题解
不知不觉NOIP正在慢慢来了啊。
最近要开始刷些和NOIP难度类似的题目。
有空做了做NOIP2017的题,感觉和NOIP2018d2的难度区分有明显的感觉啊...
d1t1 小凯的疑惑
不多说了,结论\(a * b - a - b\)。
代码略。
d1t2 时间复杂度
大模拟题,细节有点多。
不过也还好,算是比较清新的那一种。
#include <bits/stdc++.h>
const int maxl = 110;
const int inf = 0x3f3f3f3f;
bool use[40];
std::string s, code[maxl];
int n, m, i, j, k, T, l, tf, te, co, O, etot, tot; bool cf;
int f[2];
bool flagerr; char v; int For;
struct node {
int var, st, ed, id; bool f;
node() { var = st = ed = f = id = 0; }
node(int _var,int _st,int _ed,int _f,int _id) {
var = _var; st = _st; ed = _ed; f = _f; id = _id;
}
};
std::stack<node> sta;
inline void cmax(int& a,int b) {
if(a < b)
a = b;
}
inline void clear() {
memset(use,0,sizeof(use));
tf = te = 0; flagerr = 0; co = 0; O = 0;
tot = 0; cf = 1; etot = 0;
while(!sta.empty())
sta.pop();
}
inline std::pair<int,int> getinfo(std::string s) {
int l = s.size(), L = 0, O = 0; bool F = 0;
bool f1 = 0, f2 = 0;
for(int i = 0;i < l;i++) {
char c = s[i];
if(isdigit(c)) {
if(!f1 && !f2)
f1 = 1, L = L * 10 + (c & 15);
else if(f1 && !f2)
L = L * 10 + (c & 15);
else
f2 = 1, O = O * 10 + (c & 15);
} else {
if(c == 'n')
F = 1;
if(f1)
f2 = 1;
}
}
if(!F)
O = 0;
return std::make_pair(L,O);
}
inline void trans(int& x,char c) {
if(c == 'n')
x = inf;
else
x = x * 10 + (c & 15);
}
inline void getfor(std::string s) {
int l = s.size(); bool f1 = 0, f2 = 0; int now = 0;
f[0] = 0; f[1] = 0;
for(int i = 0;i < l;i++) {
char c = s[i];
if((c >= 'a' && c <= 'z') && (c != 'n'))
v = c;
else if(isdigit(c) || c == 'n') {
if(f1 && !f2)
f1 = 1, trans(f[now],c);
else
f2 = 1, trans(f[now],c);
} else if(c == ' ') {
if(i == 1)
continue;
if(!f1)
f1 = 1;
else
now = 1, f2 = 1;
}
}
return;
}
int main() {
scanf("%d",&T);
std::getline(std::cin,s);
for(;T;T--) {
clear();
std::getline(std::cin,s);
std::pair<int,int> info = getinfo(s);
int l = info.first, o = info.second;
for(int i = 1;i <= l;i++)
std::getline(std::cin,code[i]);
int t = 0;
for(int i = 1;i <= l;i++) {
if(code[i][0] == 'E') {
t--;
if(t < 0) {
flagerr = 1;
break;
}
if(sta.top().f)
co--;
if(sta.top().id == etot)
cf = 1;
use[sta.top().var] = 0;
sta.pop();
} else {
getfor(code[i]); v = v - 'a';
t++;
if(use[v]) {
flagerr = 1;
break;
}
use[v] = 1;
int lco = co;
if(f[0] <= f[1]) {
if(cf && f[1] == inf && (!(f[0] == inf && f[1] == inf)))
co++;
} else {
if(cf)
cf = 0, etot = tot + 1;
}
cmax(O,co);
sta.push(node(v,f[0],f[1],(lco == co) ^ 1,++tot));
}
// printf("%d\n",etot);
}
if(t)
flagerr = 1;
if(flagerr) {
puts("ERR"); continue;
}
if(O == o) {
puts("Yes"); continue;
} else {
puts("No"); continue;
}
}
return 0;
}
d1t3 逛公园
这题感觉没有其他人吹的那么难?
先预处理出正图中的最短路然后跑记忆化搜索。
我们先建一个反图,优化在于必然有些到不了终点的点,而反着跑可以忽略掉。
然后设当前传的参数为\(u\)和\(K\),表示当前在第\(u\)点,原题中的\(k\)还剩\(K\)
转移的时候其实就是\(dfs(v,dis[u] + K - dis[v] - w)\)
这个的意思是如果不走最短路会对\(K\)造成的贡献。
然后就结束了。
好像很好写啊?
#include <bits/stdc++.h>
const int maxn = 1e5 + 10;
typedef long long ll;
template<class t> inline void read(t& res) {
res = 0; char ch = getchar(); bool neg = 0;
while(!isdigit(ch))
neg |= ch == '-', ch = getchar();
while(isdigit(ch))
res = (res << 1) + (res << 3) + (ch & 15), ch = getchar();
if(neg)
res = -res;
}
struct edge {
int v, w;
edge() { v = w = 0; }
edge(int _v,int _w) {
v = _v; w = _w;
}
};
std::vector<edge> g1[maxn], g2[maxn];
int dis[maxn], dp[maxn][60], ans;
int n, m, i, j, k, T, mod;
bool vis[maxn], zero[maxn][60];
struct node {
int u, d;
node() { u = d = 0; }
node(int _u,int _d) {
u = _u; d = _d;
}
inline friend bool operator < (node a,node b) {
return a.d >= b.d;
}
};
inline void dijk(int S) {
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
std::priority_queue<node> pq;
pq.push(node(S,0)); dis[S] = 0;
while(!pq.empty()) {
int u = pq.top().u; pq.pop();
if(vis[u])
continue;
vis[u] = 1;
for(int i = 0, l = g1[u].size();i < l;i++) {
int v = g1[u][i].v, w = g1[u][i].w;
if(dis[u] + w < dis[v]) {
dis[v] = dis[u] + w;
pq.push(node(v,dis[v]));
}
}
}
}
int dfs(int u,int K) {
if(K < 0 || K > k)
return 0;
if(zero[u][K]) {
zero[u][K] = 0;
return -1;
}
if(~dp[u][K])
return dp[u][K];
zero[u][K] = 1; int res = 0;
for(int i = 0;i < g2[u].size();i++) {
int v = g2[u][i].v, w = g2[u][i].w;
int tmp = dfs(v,dis[u] + K - dis[v] - w);
if(tmp == -1) { zero[u][K] = 0; return -1; }
res = (res + tmp) % mod;
}
zero[u][K] = 0;
if(u == 1 && K == 0)
res++;
return dp[u][K] = res;
}
int main() {
read(T);
while(T--) {
read(n); read(m); read(k); read(mod);
for(int i = 1;i <= n;i++)
g1[i].clear(), g2[i].clear();
memset(zero,0,sizeof(zero));
memset(dp,-1,sizeof(dp));
for(int i = 1, u, v, w;i <= m;i++) {
read(u); read(v); read(w);
g1[u].push_back(edge(v,w));
g2[v].push_back(edge(u,w));
}
dijk(1); ans = 0; bool flag = 1;
for(int i = 0;i <= k;i++) {
int val = dfs(n,i); if(val == -1) { flag = 0; puts("-1"); break; }
ans = (ans + val) % mod;
}
if(flag)
printf("%d\n",ans);
}
}
d2t1 奶酪
没啥好说的,
记得判是否可以到达另外一个圆的时候要用double算,否则只有80分。
#include <bits/stdc++.h>
const int maxn = 1010;
typedef long long ll;
ll n, m, i, j, k, h, r, T;
bool flag;
bool vis[maxn];
struct node {
ll x, y, z;
node() { x = y = z = 0; }
node(ll _x,ll _y,ll _z) {
x = _x; y = _y; z = _z;
}
inline friend bool operator < (node a,node b) {
return a.z < b.z;
}
} a[maxn];
inline double dis(node p1,node p2) {
return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y) + (p1.z - p2.z) * (p1.z - p2.z));
}
void dfs(int x) {
vis[x] = 1;
if(flag)
return;
if(a[x].z + r >= h) {
flag = 1;
return;
}
for(int i = 1;i <= n;i++) {
if(flag)
return;
if(dis(a[i],a[x]) <= 2 * r && !vis[i])
dfs(i);
}
return;
}
int main() {
scanf("%lld",&T);
while(T--) {
scanf("%lld %lld %lld",&n,&h,&r);
for(int i = 1;i <= n;i++)
scanf("%lld %lld %lld",&a[i].x,&a[i].y,&a[i].z);
std::sort(a + 1,a + n + 1); bool ans = 0;
memset(vis,0,sizeof(vis)); flag = 0;
for(int i = 1;i <= n;i++) {
if(a[i].z - r <= 0) {
dfs(i);
if(flag)
break;
}
}
puts(flag ? "Yes" : "No");
}
return 0;
}
d2t2 宝藏
有点不是很想写诶,直接放个我远古时期的讲解吧。
here
当年为什么我一定要加前向星我并不知道,
想一想就是那个错误的解法啊,
只是碰巧过了hack数据而已....
嘛不管了。
d2t3 列队
思路很简单但是代码有点难写的平衡树题。(标算的树状数组一看就让人头晕于是就没研究)。
对于\(1 \sim n - 1\) 单独维护一个无旋\(treap\)
然后最后一列单独处理。
按题目中的操作进行分裂合并即可。
贴个远古时期的代码。
#include <bits/stdc++.h>
#define il inline
#define int long long
typedef long long ll;
const int maxn = 3e5 + 10;
const int maxo = maxn * 20;
using namespace std;
int n,m,i,j,k,Task,ocnt;
ll ch[maxo][2]; ll l[maxo],r[maxo],size[maxo],rnd[maxo],root[maxn];
namespace Fast_Input {
template<class T> il void read(T& res) {
res = 0; char ch; bool sign = 0;
do { ch = getchar(); sign |= ch == '-'; } while(!isdigit(ch));
while(isdigit(ch)) res = (res << 1) + (res << 3) + (ch & 15),ch = getchar();
(sign) && (res = -res);
return;
}
}
using Fast_Input::read;
il ll new_node(int L,int R) {
ll o = ++ocnt;
l[o] = L; r[o] = R; size[o] = R - L + 1; rnd[o] = rand();
return o;
}
il void push_up(ll o) {
size[o] = size[ch[o][0]] + size[ch[o][1]] + r[o] - l[o] + 1;
}
int merge(int u,int v) {
if(!u) return v; if(!v) return u;
if(rnd[u] < rnd[v]) {
ch[u][1] = merge(ch[u][1],v);
push_up(u);
return u;
} else {
ch[v][0] = merge(u,ch[v][0]);
push_up(v);
return v;
}
}
il void split1(ll x,ll y) {
if(y >= r[x] - l[x] + 1) return; int t = l[x] + y - 1;
int o = new_node(t + 1,r[x]); r[x] = t;
ch[x][1] = merge(o,ch[x][1]); push_up(x);
return;
}
void split2(int u,int k,ll& x,ll& y) {
if(!u) return (void)(x = y = 0);
if(k <= size[ch[u][0]]) {
y = u; split2(ch[u][0],k,x,ch[u][0]);
} else {
split1(u,k - size[ch[u][0]]); x = u;
split2(ch[u][1],k - size[ch[u][0]] - r[u] + l[u] - 1,ch[u][1],y);
}
push_up(u);
return;
}
signed main() {
srand((unsigned)time(NULL));
read(n); read(m); read(Task);
for(int i = 1;i <= n;i++) {
root[i] = new_node((i - 1) * m + 1,i * m - 1);
}
for(int i = 1;i <= n;i++) {
root[n + 1] = merge(root[n + 1],new_node(i * m,i * m));
}
for(ll x,y,a,b,c,aa,bb,cc;Task;--Task) {
read(x); read(y);
if(y != m) {
split2(root[x],y,a,b);
split2(a,y - 1,a,c);
printf("%lld\n",l[c]);
split2(root[n + 1],x,aa,bb);
split2(aa,x - 1,aa,cc);
root[x] = merge(a,merge(b,cc));
root[n + 1] = merge(aa,merge(bb,c));
} else {
split2(root[n + 1],x,a,b);
split2(a,x - 1,a,c);
printf("%lld\n",l[c]);
root[n + 1] = merge(a,merge(b,c));
}
}
return 0;
}