NOI Online 2020提高组游记

前言

自从疫情以来,停止集训,GDKOI也取消了(本蒟蒻去了还是受虐),打比赛的机会也就只有洛谷月赛爆零和水codeforces去了。
还好有这场NOI Online的比赛叫醒了我,让我看到我和其他选手的巨大差距,不然我还在梦游中。
不要问我这等蒟蒻怎么有机会参加NOI Online,我的能力不足以参加。

比赛

早上,起床。
打开电脑死都登不上网站。刷了半个小时才登上,心烦意乱。
打开题面。
以下是我当时的思路:

T1

看着T1好可做的样子,拿着一张纸推结论去了。
哇塞,我们可以互减一下,然后让数组结果为零不就是了?真好。我还推到了两种操作的转化公式,操作2一加一减,一加起来和一定?嗯,最后能够消成一个方程组,就推数论去了。

额,方程组的数量不一定啊?那么,在少于系数的时候,我们需要化简成多元不定方程,我们设一个数消掉其中两个变量,不断地消,最后变成原变量数-1组方程,贝祖来判,带回去不断贝祖,这样判到一个通解。

如果多余的话呢?像以前一样解出通解式,然后把多余的带入通解式再看看符不符合就是啦!
嗯,好棒的O(m)思路。

然后我就离正解越来越远了。

T2

T1花了这么多时间???赶紧看看T2。嗯,冒泡有很有意思的性质诶。然后我居然想到了归并(看我很蒟蒻吧),开始思路居然好好的,到最后发现还是处理不了……崩了崩了。

赶紧换用线段树,嗯,写得挺顺利的,只不过能把线段树递归写错而已。
第一个数一定会往后撞对嘛,撞到一个比它大的数就要停下来了对吧,期间碾压过的每个数逆序对减一对吧,然后位置前移对吧。
我:”哇塞线段树区间前移好牛逼的操作,我不会诶。“然后就开始考虑前移相互位置的规律了,额,每个数之间的位置,到自己前面停下来相对于其他数的位置变化,额……

要是谁会区间平移的话,这就是道普及。看来,只有万能平衡树了(幸好自己没敲)。

突然发现离比赛不多了,T3还没做,我……
写了个暴力交了,纯纯的,入门难度的暴力。

然后,网就断了。(我:……)

赛后

我好快乐,因为我是七彩橡胶皮球人。

从来没有觉得,自己离咸鸭蛋如此近过。我挂在NOI Online上了。只要是个人写个暴力,分数都比我高,诶。
看看题解,发现T1是个图论而非数论。我恍然大雾了。
额,没什么要说了,自己太菜了,我真的离我的同学,还差得很远。
自己,不够努力。
奉上赛后改正的代码。

T1

#include
#include

#define ll long long

using namespace std;

const int N=1e5+10;

int T, n, m, cnt;
int a[N], b[N], fr[N], to[N], f[N], vis[N];
ll sum[N], sum2[3];
bool solve;
vector<int>v[N];

int getfa(int x) {
	return (f[x]==x)?f[x]:(f[x]=getfa(f[x]));
}

bool dfs(int x, int flag) {
	vis[x]=flag, sum2[flag]+=sum[x];
	bool ok=1;
	for (int i = 0; i < v[x].size(); i++) {
		int y = v[x][i];
		if (vis[y] == vis[x]) ok = 0;
		if (!vis[y] && !dfs(y, 3-flag)) ok = 0;
	}
	return ok;
}

int main() {
	scanf("%d", &T);
	while(T--) {
		scanf("%d%d", &n, &m);
		for(int i=1; i<=n; i++) scanf("%d", &a[i]);
		for(int i=1; i<=n; i++) scanf("%d", &b[i]);
		cnt=0, solve=true;
		for(int i=1; i<=n; i++) {
			f[i]=i;
			sum[i]=vis[i]=0;
			v[i].clear();
		}
		for(int i=1; i<=m; i++) {
			int flag, x, y;
			scanf("%d%d%d", &flag, &x, &y);
			if(flag==2) f[getfa(x)]=getfa(y);
			else fr[++cnt]=x, to[cnt]=y;
		}
		for(int i=1; i<=n; i++) {
			sum[getfa(i)]+=a[i]-b[i];
		}
		for(int i=1; i<=cnt; i++) {
			int x=getfa(fr[i]), y=getfa(to[i]);
			v[x].push_back(y), v[y].push_back(x);
		}
		for(int i=1; i<=n; i++) {
			if(getfa(i)==i&&!vis[i]) {
				sum2[1]=sum2[2]=0;
				bool ok = dfs(i, 1);
				if (ok && sum2[1] != sum2[2]) solve=false;
				if (!ok && ((sum2[1] ^ sum2[2]) & 1)) solve=false;
			}
		}
		if(solve) {
			puts("YES");
		} else {
			puts("NO");
		}
	}
}

T2

#include
#include

#define ll long long
#define lowbits(x) (x&(-x))

using namespace std;

const int N=2e5+10;
int a[N], n, m;
int sum1[N];

void add1(int x, int i) {
	if(!x) return;
	while(x<=n) {
		sum1[x]+=i;
		x+=lowbits(x);
	}
}

int query1(int x) {
	int ans=0;
	while(x) {
		ans+=sum1[x];
		x-=lowbits(x);
	}
	return ans;
}

int c[N];

ll sum[N];
int cnt[N];

void add(int val, int flag) {
	if(!val) return;
	int x=val;
	while(x<=n) {
		sum[x]+=1ll*val*flag;
		cnt[x]+=flag;
		x+=lowbits(x);
	}
}

ll qsum(int x) {
	ll ans=0;
	while(x) {
		ans+=sum[x];
		x-=lowbits(x);
	}
	return ans;
}

int qcnt(int x) {
	int ans=0;
	while(x) {
		ans+=cnt[x];
		x-=lowbits(x);
	}
	return ans;
}

int main() {
	scanf("%d%d", &n, &m);
	for(int i=1; i<=n; i++) {
		scanf("%d", &a[i]);
		c[i]=query1(n)-query1(a[i]);
		add(c[i], 1);
		add1(a[i], 1);
	}
	while(m--) {
		int flag, x;
		scanf("%d%d", &flag, &x);
		if(flag==1) {
			if(a[x]<a[x+1]) {
				add(c[x], -1);
				add(++c[x],1);
				swap(a[x], a[x+1]);
				swap(c[x], c[x+1]);
			} else {
				swap(a[x], a[x+1]);
				swap(c[x], c[x+1]);
				add(c[x], -1);
				add(--c[x],1);
			}
		} else {
			printf("%lld\n", (x>n)?0ll:qsum(n)-qsum(x)-1ll*x*(qcnt(n)-qcnt(x)));
		}
	}
}

最后,希望我和各位以后都不要重蹈我这次的覆辙,祝愿大家在各自的道路上越走越远。

你可能感兴趣的:(游记)