noi 2017 简要题解

回顾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("");
	}
}



蔬菜

这篇博客说得很好
最后可以用并查集维护。
把总量拆成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]]);
}

分身术

暂时留坑

代码更新中

总结

想题要给自己定时,找性质再确认结论,要把式子推清楚

如果不推清楚式子,只是大概是这样,想了没有任何意义。必须踏踏实实把式子推完整,把细节想清楚!

猜你喜欢

转载自blog.csdn.net/weixin_42484877/article/details/86259079