鞍山一中学科网 信奥专题 noi 2017 简要题解

noi 2017 简要题解

广告位4

回顾noi 2017
DAY1
整数
压位维护序列。用线段树维护一段0后第一个1,一段A-1后第一个<(A – 1)的数
推推转移式子即可

代码犯了无数个错误,并且检查的时候没有仔细的检查出来,仍然借助gdb和对拍才调出来!非常糟糕!
**1. 线段树初始化错误
**2. 打错变量名3处
3. 进位后写成减M,应该是M+1
4. 很多地方一开始想清楚,改的时候没有想清楚,反而改错。比如二分是找最低位非满或非0.修改的时候一定要想清楚,很多时候找到错误反而没有全面的修改,浪费了更多时间
写代码前细节在头脑中不清晰,代码写得太少了!

#include<bits/stdc++.h>
using namespace std;
#define maxn 1002020
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i–)
#define rvc(i,S) for(register int i = 0 ; i < (int)S.size() ; i++)
#define rvcd(i,S) for(register int i = ((int)S.size()) – 1 ; i >= 0 ; i–)
#define fore(i,x)for (register int i = head[x] ; i ; i = e[i].next)
#define pb push_back
#define prev prev_
#define stack stack_
#define mp make_pair
#define fi first
#define se second
#define inf 0x3f3f3f3f
typedef long long ll;
typedef pair<int,int> pr;

const ll M = (1 << 30) – 1,K = 30;
int cov[maxn << 2],tag[maxn << 2],val[maxn << 2];
// 1 : 全为M
// 2 :全为 0
// 0 :无性质
int n,t1,t2,t3,L;

void build(int x,int l,int r){
tag[x] = 2 , cov[x] = -1;
if ( l == r ) return;
int mid = (l + r) >> 1;
build(x << 1,l,mid) , build((x << 1) | 1,mid + 1,r);
}
inline void cover(int x,int d){
cov[x] = d , val[x] = d;
if ( d == M ) tag[x] = 1;
else tag[x] = 2;
}
inline void pushdown(int x){
int ls = x << 1,rs = (x << 1) | 1;
if ( cov[x] != -1 ){
cover(ls,cov[x]);
cover(rs,cov[x]);
cov[x] = -1;
}
}
inline void update(int x){
int ls = x << 1,rs = (x << 1) | 1;
if ( tag[ls] == 1 && tag[rs] == 1 ) tag[x] = 1;
else if ( tag[ls] == 2 && tag[rs] == 2 ) tag[x] = 2;
else tag[x] = 0;
}
void modify(int x,int l,int r,int L,int R,int d){
if ( L > R ) return;
if ( L <= l && R >= r ){
cover(x,d);
return;
}
int ls = x << 1,rs = (x << 1) | 1,mid = (l + r) >> 1;
pushdown(x);
if ( L <= mid ) modify(ls,l,mid,L,R,d);
if ( R > mid ) modify(rs,mid + 1,r,L,R,d);
update(x);
}
void modify(int x,int l,int r,int id,int d){
if ( l == r ){
val[x] += d;
if ( val[x] == M ) tag[x] = 1;
else if ( val[x] == 0 ) tag[x] = 2;
else tag[x] = 0;
return;
}
int ls = x << 1,rs = (x << 1) | 1,mid = (l + r) >> 1;
pushdown(x);
if ( id <= mid ) modify(ls,l,mid,id,d);
else modify(rs,mid + 1,r,id,d);
update(x);
}
int find1(int x,int l,int r,int L,int R){
if ( tag[x] == 2 ) return 0;
if ( l == r ) return l;
int ls = x << 1,rs = (x << 1) | 1,mid = (l + r) >> 1;
pushdown(x);
if ( L <= l && R >= r ){
if ( tag[ls] == 2 ) return find1(rs,mid + 1,r,L,R);
return find1(ls,l,mid,L,R);
}
int p = 0;
if ( L <= mid ) p = find1(ls,l,mid,L,R);
if ( p ) return p;
return find1(rs,mid + 1,r,L,R);
}
int find0(int x,int l,int r,int L,int R){
if ( tag[x] == 1 ) return 0;
if ( l == r ) return l;
int ls = x << 1,rs = (x << 1) | 1,mid = (l + r) >> 1;
pushdown(x);
if ( L <= l && R >= r ){
if ( tag[ls] == 1 ) return find0(rs,mid + 1,r,L,R);
return find0(ls,l,mid,L,R);
}
int p = 0;
if ( L <= mid ) p = find0(ls,l,mid,L,R);
if ( p ) return p;
return find0(rs,mid + 1,r,L,R);
}
int query(int x,int l,int r,int id){
if ( l == r ) return val[x];
int ls = x << 1,rs = (x << 1) | 1,mid = (l + r) >> 1;
pushdown(x);
if ( id <= mid ) return query(ls,l,mid,id);
return query(rs,mid + 1,r,id);
}
int main(){
// freopen(“input.txt”,”r”,stdin);
scanf(“%d %d %d %d”,&n,&t1,&t2,&t3);
L = n + 1000;
build(1,0,L);
while ( n– ){
int t,a,b,k;
scanf(“%d”,&t);
if ( t == 1 ){
scanf(“%d %d”,&a,&b);
if ( a < 0 ){
a = -a;
int x = b / K,y = b % K,t = 0,vx = query(1,0,L,x),vy,delta;
ll cur = (ll)a << y;
delta = cur & M;
if ( vx < delta ) delta -= M + 1 , t++;
if ( t || cur >> K ){
vy = query(1,0,L,x + 1);
int delta2 = t + (cur >> K);
if ( vy < delta2 ){
int p = find1(1,0,L,x + 2,L);
modify(1,0,L,x + 2,p – 1,M);
modify(1,0,L,p,-1);
delta2 -= M + 1;
}
modify(1,0,L,x + 1,-delta2);
}
modify(1,0,L,x,-delta);
}
else{
int x = b / K,y = b % K,t = 0,vx = query(1,0,L,x),vy,delta;
ll cur = (ll)a << y;
delta = cur & M;
if ( vx + delta > M ) delta -= M + 1 , t++;
if ( t || cur >> K ){
vy = query(1,0,L,x + 1);
int delta2 = t + (cur >> K);
if ( vy + delta2 > M ){
int p = find0(1,0,L,x + 2,L);
modify(1,0,L,x + 2,p – 1,0);
modify(1,0,L,p,1);
delta2 -= M + 1;
}
modify(1,0,L,x + 1,delta2);
}
modify(1,0,L,x,delta);
}
}
else{
scanf(“%d”,&a);
int x = a / K;
int d = query(1,0,L,x);
printf(“%d\n”,(d >> (a % K)) & 1);
}
}
}

 

 

蚯蚓排队
维护hash值,map到1023333的hash表里。
每次分裂只会有k2个串被删掉,合并又至多多出k2个。
O(nk+k^2 * c)

用了两种hash表,还是过不了uoj的extra test,好像还要读入优化,不想卡常了

#include<bits/stdc++.h>
using namespace std;
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i–)
#define rvc(i,S) for(register int i = 0 ; i < (int)S.size() ; i++)
#define rvcd(i,S) for(register int i = ((int)S.size()) – 1 ; i >= 0 ; i–)
#define fore(i,x) for(register int i = head[x] ; i ; i = e[i].next)
#define pb push_back
#define prev prev_
#define stack stack_
#define inf 0x3f3f3f3f
#define maxn 200020
#define N 10000020
#define mp make_pair
#define fi first
#define se second
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pr;

struct hashmap
{
int head[10233333] , len[20000010] , cnt[20000010] , next[20000010] , tot;
ull val[20000010];
inline void insert(int l , ull v)
{
int x = v % 10233333 , i , last = 0;
for(i = head[x] ; i ; last = i , i = next[i])
if(len[i] == l && val[i] == v)
break;
if(i) cnt[i] ++ ;
else
{
if(!last) head[x] = ++tot;
else next[last] = ++tot;
len[tot] = l , val[tot] = v , cnt[tot] ++ ;
}
}
inline void erase(int l , ull v)
{
int x = v % 10233333 , i;
for(i = head[x] ; i ; i = next[i])
if(len[i] == l && val[i] == v)
cnt[i] — ;
}
inline int find(int l , ull v)
{
int x = v % 10233333 , i;
for(i = head[x] ; i ; i = next[i])
if(len[i] == l && val[i] == v)
return cnt[i];
return 0;
}
}rec;
const int MAXK = 50;
const int MAXN = 200005;
const int MAXL = 1e7 + 5;
const int MAXS = 2e7 + 5;
const int HASH = 1e7 + 19;
struct HashTable {
int tot, head[HASH];
int val[MAXS], nxt[MAXS];
ull mask[MAXS];
void insert(ull msk, int value) {
int p = head[msk % HASH];
while (p != 0) {
if (mask[p] == msk) {
val[p] += value;
return;
}
p = nxt[p];
}
tot++;
mask[tot] = msk;
val[tot] = value;
nxt[tot] = head[msk % HASH];
head[msk % HASH] = tot;
}
int query(ull msk) {
int p = head[msk % HASH];
while (p != 0) {
if (mask[p] == msk) return val[p];
p = nxt[p];
}
return 0;
}
} HT;
const ull base = 97;
const ll mod = 998244353;
int n,m,a[maxn],pre[maxn],post[maxn];
char ch[maxn];
ull pow_[maxn];

void init(){
pow_[0] = 1;
rep(i,1,100) pow_[i] = pow_[i – 1] * base;
}
void merge(int x,int y){
int t = 1,id = x; ull cur = 0;
while ( t <= 49 ){
cur += pow_[t – 1] * a[id];
int id2 = y; ull cur2 = cur;
rep(j,1,50 – t){
cur2 = cur2 * base + a[id2];
// rec.insert(t + j,cur2);
HT.insert(cur2,1);
id2 = post[id2];
if ( !id2 ) break;
}
id = pre[id] , ++t;
if ( !id ) break;
}
post[x] = y , pre[y] = x;
}
void split(int x){
int t = 1,id = x; ull cur = 0;
while ( t <= 49 ){
cur += pow_[t – 1] * a[id];
int id2 = post[x]; ull cur2 = cur;
rep(j,1,50 – t){
cur2 = cur2 * base + a[id2];
// rec.erase(t + j,cur2);
HT.insert(cur2,-1);
id2 = post[id2];
if ( !id2 ) break;
}
id = pre[id] , ++t;
if ( !id ) break;
}
pre[post[x]] = 0 , post[x] = 0;
}
int main(){
init();
scanf(“%d %d”,&n,&m);
rep(i,1,n){
scanf(“%d”,&a[i]);
// rec.insert(1,a[i]);
HT.insert(a[i],1);
}
rep(i,1,m){
int t,x,y,k;
scanf(“%d”,&t);
if ( t == 1 ){
scanf(“%d %d”,&x,&y);
merge(x,y);
}
else if ( t == 2 ){
scanf(“%d”,&x);
split(x);
}
else{
scanf(“%s%d”,ch + 1,&k);
int l = strlen(ch + 1); ull cur = 0; ll ans = 1;
rep(i,1,l){
cur = cur * base + (ch[i] – ‘0’);
if ( i >= k ){
// ans = ans * rec.find(k,cur) % mod;
ans = ans * HT.query(cur) % mod;
cur -= pow_[k – 1] * (ch[i – k + 1] – ‘0’);
}

}
printf(“%lld\n”,ans);
}
}
}

泳池
直接用大佬的吧
感觉dp还有一种推法,从左往右dp,但是方程没有这么清晰
常系数递推可以用特征多项式优化

DAY2
游戏
枚举x是a还是b型,转换成2-SAT
学到了2-SAT新姿势,输出方案直接比较tarjian后强连通分量的标号,因为标号自然构成逆拓扑序,选标号较小的即可
2-SAT连边的时候,若x1 必须选 y2 ,则 y1 必须选 x2,不能漏连,还要判是否是对同一张图的限制
不要卡常了!平时做题卡常没有意义,把时间花在想和写更多题

#include<bits/stdc++.h>
using namespace std;
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i–)
#define rvc(i,S) for(register int i = 0 ; i < (int)S.size() ; i++)
#define rvcd(i,S) for(register int i = ((int)S.size()) – 1 ; i >= 0 ; i–)
#define fore(i,x) for(register int i = head[x] ; i ; i = e[i].next)
#define pb push_back
#define prev prev_
#define stack stack_
#define inf 0x3f3f3f3f
#define maxn 200020
#define mp make_pair
#define fi first
#define se second
typedef long long ll;
typedef pair<int,int> pr;

struct node{
int next,to;
}e[maxn * 2],e2[maxn * 2];
struct node2{
int x,y;
int ax,ay;
}dt[maxn];

int head[maxn],cnt,head2[maxn];
int dfn[maxn],ins[maxn],belong[maxn],sz[maxn],low[maxn],st[maxn],tops,dfstime;
int n,m,k,tot,id[maxn],del[maxn],flag;
char ch[maxn],ans[maxn];
pr cur[maxn];

inline void adde(int x,int y){
if ( x == y ) return;
e[++cnt].to = y;
e[cnt].next = head[x];
head[x] = cnt;
}
void dfs(int x){
dfn[x] = low[x] = ++dfstime;
ins[x] = 1 , st[++tops] = x;
fore(i,x){
if ( !dfn[e[i].to] ) dfs(e[i].to) , low[x] = min(low[x],low[e[i].to]);
else if ( ins[e[i].to] ) low[x] = min(low[x],low[e[i].to]);
}
if ( low[x] == dfn[x] ){
int y = st[tops–]; ++tot;
while ( y != x ){
belong[y] = tot , ins[y] = 0;
y = st[tops–];
}
belong[x] = tot , ins[x] = 0;
}
}
void init(){
rep(i,1,n){
if ( ch[i] == ‘a’ ) del[i] = 1 , cur[i] = mp(i + n,i + n * 2);
else if ( ch[i] == ‘b’ ) del[i + n] = 1 , cur[i] = mp(i,i + n * 2);
else if ( ch[i] == ‘c’ ) del[i + n * 2] = 1 , cur[i] = mp(i + n,i);;
}
}
void build(){
cnt = tot = dfstime = 0;
rep(i,1,n * 3) dfn[i] = low[i] = belong[i] = head[i] = 0;
rep(x,1,k){
int i = id[x];
del[i] = del[i + n] = del[i + n * 2] = 0;
if ( ch[i] == ‘a’ ) del[i] = 1 , cur[i] = mp(i + n,i + n * 2);
else if ( ch[i] == ‘b’ ) del[i + n] = 1 , cur[i] = mp(i,i + n * 2);
else if ( ch[i] == ‘c’ ) del[i + n * 2] = 1 , cur[i] = mp(i + n,i);;
}
rep(i,1,m){
int x = dt[i].x,y = dt[i].y, ax = dt[i].ax , ay = dt[i].ay;
if ( del[ax] ) continue;
if ( del[ay] ){
// rep(i,0,2) if ( !del[i * n + x] && i * n + x != ax ) adde(ax,i * n + x);
adde(ax,cur[x].fi) , adde(ax,cur[x].se);
}
else{
adde(ax,ay);
if ( x != y ){
// rep(i,0,2) if ( !del[i * n + x] && i * n + x != ax )
// rep(j,0,2) if ( !del[j * n + y] && j * n + y != ay )
// adde(j * n + y,i * n + x);
if ( ax == cur[x].fi ) {
if ( ay == cur[y].fi ) adde(cur[y].se,cur[x].se);
else adde(cur[y].fi,cur[x].se);
} else{
if ( ay == cur[y].fi ) adde(cur[y].se,cur[x].fi);
else adde(cur[y].fi,cur[x].fi);
}
}
}
}
}
bool check(){
build();
rep(i,1,n * 3) if ( !del[i] && !dfn[i] ) dfs(i);
rep(i,1,n){
if ( ch[i] == ‘a’ ){
if ( belong[i + n] == belong[i + n * 2] ) return 0;
if ( belong[i + n] < belong[i + n * 2] ){
ans[i] = ‘B’;
}
else ans[i] = ‘C’;
}
else if ( ch[i] == ‘b’ ){
if ( belong[i] == belong[i + n * 2] ) return 0;
if ( belong[i] < belong[i + n * 2] ){
ans[i] = ‘A’;
}
else ans[i] = ‘C’;
}
else{
if ( belong[i + n] == belong[i] ) return 0;
if ( belong[i + n] < belong[i] ){
ans[i] = ‘B’;
}
else ans[i] = ‘A’;
}
}
return 1;
}
void dfs_x(int x){
if ( x == k + 1 ){
if ( check() ) flag = 1;
return;
}
ch[id[x]] = ‘a’;
dfs_x(x + 1);
if ( flag ) return;
ch[id[x]] = ‘b’;
dfs_x(x + 1);
}
int main(){
scanf(“%d %d”,&n,&k);
scanf(“%s”,ch + 1);
int cur = 0;
rep(i,1,n) if ( ch[i] == ‘x’ ) id[++cur] = i;
scanf(“%d”,&m);
rep(i,1,m){
int x,y; char ax[10],ay[10];
scanf(“%d%s%d%s”,&x,ax,&y,ay);
dt[i] = (node2){x,y,(ax[0] – ‘A’) * n + x,(ay[0] – ‘A’) * n + y};
}
init();
dfs_x(1);
if ( !flag ) puts(“-1”);
else{
rep(i,1,n) printf(“%c”,ans[i]);
puts(“”);
}
}

 

1
蔬菜
这篇博客说得很好
最后可以用并查集维护。
把总量拆成c份,每天的增量是当天坏掉的,这个思路很巧。可以这么拆是因为题目中每天坏掉的是固定的那几个,而卖出后就不会坏了。
读题要看样例解释!!
一开始想错了,以为大的一定先选
不知道为什么一定要倒过来求所有的答案,感觉正着每次选最大的m个也是一样的

update: 正着做的确可以。其实原理一样
打错了变量,并查集在x = 0时特判少了,检查代码仍然没有读出来,又借助对拍,非常糟糕!
静下来读代码!!

#include<bits/stdc++.h>
using namespace std;
#define rep(i,l,r) for(register int i = l ; i <= r ; i++)
#define repd(i,r,l) for(register int i = r ; i >= l ; i–)
#define rvc(i,S) for(register int i = 0 ; i < (int)S.size() ; i++)
#define rvcd(i,S) for(register int i = ((int)S.size()) – 1 ; i >= 0 ; i–)
#define fore(i,x) for(register int i = head[x] ; i ; i = e[i].next)
#define pb push_back
#define prev prev_
#define stack stack_
#define inf 0x3f3f3f3f
#define maxn 200020
#define mp make_pair
#define fi first
#define se second
typedef long long ll;
typedef pair<int,int> pr;

struct node{
int a,c,tp,t,x;
bool operator < (node cur)const{
return a < cur.a;
}
};
priority_queue <node> heap;
int n,m,mx,q,p[maxn];
int last[maxn];
int g[maxn * 10],cnt,num[maxn];
ll ans[maxn];
int getlast(int x){
if ( x > mx ) return getlast(mx);
return x == last[x] ? x : last[x] = getlast(last[x]);
}
int main(){
scanf(“%d %d %d”,&n,&m,&q);
rep(i,1,n){
int a,x,s,c;
scanf(“%d %d %d %d”,&a,&s,&c,&x);
heap.push((node){a + s,1,1,x ? (c – 1) / x + 1 : 100000,x});
if ( c > 1 ) heap.push((node){a,c – 1,0,x ? (c – 2) / x + 1 : 100000,x});
}
rep(i,1,q) scanf(“%d”,&p[i]) , mx = max(mx,p[i]);
rep(i,1,mx) last[i] = i , num[i] = m;
while ( heap.size() ){
node cur = heap.top(); heap.pop();
int a = cur.a , c = cur.c , tp = cur.tp , t = cur.t , x = cur.x , p = 0 , np;
if ( tp ){
p = getlast(t);
if ( p ){
g[++cnt] = a;
num[p]–;
if ( !num[p] ) last[p] = getlast(p – 1);
}
}
else{
if ( !x ) p = mx;
else p = t , c -= (p – 1) * x;
np = getlast(p) , c += (p – np) * x , p = np;
while ( p ){
if ( !c ) break;
int delta = min(num[p],c);
rep(i,1,delta) g[++cnt] = a;
c -= delta , num[p] -= delta;
if ( !num[p] ) last[p] = getlast(p – 1);
np = getlast(p – 1) , c += (p – np) * x , p = np;
}
}
}
ll sum = 0;
rep(i,1,cnt){
sum += g[i];
ans[(int)ceil((double)i / m)] = sum;
}
rep(i,1,mx) ans[i] = max(ans[i],ans[i – 1]);
rep(i,1,q) printf(“%lld\n”,ans[p[i]]);
}

分身术
暂时留坑

代码更新中
总结
想题要给自己定时,找性质再确认结论,要把式子推清楚

如果不推清楚式子,只是大概是这样,想了没有任何意义。必须踏踏实实把式子推完整,把细节想清楚!
————————————————
版权声明:本文为CSDN博主「Thomas_ZQQ@Runespoor」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42484877/article/details/86259079

广告位5
noi 2017 简要题解

作者: flybird

上一篇
下一篇

发表评论

联系我们

联系我们

0412-5227722

在线咨询: QQ交谈

邮箱: 81689132@qq.com

我啊
关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部