2020.8.19练习赛

2020.8.19练习赛

干哦兄弟这个题目怎么全是私货啊

T1 漆黑列车载运数个谎言

问题描述

二战前夕的苏瓦尔由科学院和灵异部掌控,他们为此展开了激烈的争夺。在 \(Masquerade\) 号列车上,收集了重要情报的 \(N\) 个人(编号为 \(1..N\) )需要想方设法交换情报,但是不能把情报泄露给敌对势力。

其中,第 \(i\) 个人的情报为:如果 \(P_i\) 成立,那么 \(Q_i\) 成立。 我们不妨用小写字母 \(x\) 表示编号为 \(x\) 的人,小写字母 \(y\) 表示编号为 \(y\) 的人。

\(y\) 询问 \(x\) 的情报时,\(x\) 为了保证情报不被泄露,不能直接对 \(y\) 说出自己情报的具体内容,因此,\(x\) 只会对 \(y\) 回答一个 \(0\)\(2\) 之间的整数:

\(0\) 表示:“我的情报等价于你的情报。”

\(1\) 表示:“我的情报是你的情报的逆命题。”

\(2\) 表示:“我的情报是你的情报的否命题。”

其中,逆命题、否命题的定义详见样例说明。

为了保证自身的安全,每个人只会打探编号比自己小的人的情报,且每个人至多只会打探一个人的情报。也就是说,如果 \(y\) 打探了 \(x\) 的情报,那么必然有 \(x\) 小于 \(y\),且 \(y\) 之后就不会再打探任何人的情报。

在他们刺探情报的过程中,维多利加想要解决这样的两类问题:

\(A\).如果 \(x\) 的情报一定正确,那么 \(y\) 的情报是否一定正确。

\(B\).如果 \(x\) 的情报一定错误,那么 \(y\) 的情报是否一定错误。

作为维多利加的伙伴久城一弥,你能帮她解决这些问题吗?

输入格式

第一行两个整数 N,M,表示列车上有 N 个人,总共有 M 个操作。

接下来 M 行,每行三个整数 op,x,y,按照时间顺序给出问题描述中的操作。

若 0≤op≤2,则表示 y 打探 x 的情报时,x 的回答为 op。

若 3≤op≤4,则表示维多利加想要问你问题:3 表示 A 类问题,4 表示 B 类问题

样例

输入:

7 10
1 1 2
1 2 3
3 1 3
4 1 5
2 3 4
2 4 5
2 2 6
4 1 5
3 2 6
3 1 7

输出:

1
0
1
0
0

解释:

对于命题:“如果 P 成立,那么 Q 成立。”

它的逆命题为:“如果 Q 成立,那么 P 成立。”

它的否命题为:“如果 P 不成立,那么 Q 不成立。”

显然,一个命题的逆命题的逆命题是它自身,一个命题的否命题的否命题也是它自身。

最终情况下,设 1 的情报为:如果 P 成立,那么 Q 成立。

则 2 的情报为:如果 Q 成立,那么 P 成立。

3 的情报为:如果 P 成立,那么 Q 成立。

4 的情报为:如果 P 不成立,那么 Q 不成立。

5 的情报为:如果 P 成立,那么 Q 成立。

6 的情报为:如果 Q 不成立,那么 P 不成立。

7 的情报不能确定。

注意:每次询问只能以在它前面的操作为依据,不能以在它后面的操作为依据。即: 询问是在线的。

范围限制

10%的数据保证,N≤20,M≤20。

另有 10%的数据保证,N=M,且前 M−1 个操作均不含 3,4 操作。

另有 10%的数据保证,M 个操作中不出现 1,2 操作。

另有 10%的数据保证,M 个操作中不出现 1 操作,\(N≤10^3,M≤10^3\)

另有 20%的数据保证,M 个操作中不出现 2 操作。

另有 20%的数据保证,M 个操作随机生成。

100%的数据保证,\(N≤10^6\)\(M≤10^6\)

题解

看到这个题,其实我是绝望的。因为我命题没学好(而且数学老师还默认我们学过了所以没讲)。但我还是现场推了一下成立情况然后水过了70分。所以现推结论才是王道

我们看到给出的操作编号中等价、逆、否命题(个人习惯称为 \(act\) )为\(0,1,2\),它们在二进制下分别为 \(0,1,10\)。于是,我们很容易能发现,可以用0,1,2这三个数字的异或值得到一个命题被操作数次之后的所有情况:

  • 等价命题:\(0\)(二进制为\(0\)
  • 逆命题:\(1\)(二进制为\(1\)
  • 否命题:\(2\)(二进制为\(10\)
  • 逆否命题:\(3\)(二进制为\(11\)

根据命题的知识,我们可知:

  • 一个命题的逆命题的逆命题为它的等价命题
  • 一个命题的否命题的否命题为它的等价命题
  • 一个命题的逆否命题为它的等价命题

同时,\(A\)操作相当于询问\(x,y\)是否为等价命题,\(B\)操作相当于询问\(y\)是否是\(x\)的否命题的否命题,所以\(A,B\)是等价的,可以放在一起处理(考场上我没想到这点)。

再看到我们推出的每种命题的数字,我们可以发现,当一个命题\(P\)和另一个命题\(Q\)等价时,

\(P\) ^ \(3 = Q\)\(P = Q\)

于是题意明了了。我们需要维护多个集合,每个集合都与自己的根有固定的异或关系。每次操作会连接两个集合,其中一个集合的根将会失去根的身份,并获得与另外一个集合的根的关系。失去根的集合中的元素同样也会获得这个关系。

所以,带权并查集走起。每个节点的点权就是他与自己原来父亲节点的关系(即互为哪种命题)。在线修改在线询问。

做完力。把数组开大点即可通过此题。

KONO代码哒!

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC target("avx")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")
using namespace std;
char *p1,*p2,buf[1<<20];

#define GC (p1==p2&&(p1=buf,p2=buf+fread(buf,1,1<<20,stdin),p1==p2)?0:(*(p1++)))
//#define GC getchar()
inline int in()
{
    int ans;
    char t,k;
    while(((t=GC)!='-'&&(t>'9'||t<'0')));
    k=(t=='-');
    ans=k?0:(t-'0');
    while((t=GC)>='0'&&t<='9')ans=ans*10+t-'0';
    return k?-ans:ans;
}
const int maxn=1000010;
int n,m;
int col[maxn];
bool mark[maxn];
int f[maxn];
int dis[maxn];
int gf(int x) {
	if(f[x]!=x) {
		int rt=f[x];
		f[x]=gf(f[x]);
		dis[x]^=dis[rt];
	}
	return f[x];
}
void fin()
{
	freopen("liar.in","r",stdin);
	freopen("liar.out","w",stdout);
}
int main() {
//	fin();
	n=in();m=in();
	int i,j;
	for(i=1; i<=n; i++)f[i]=i;
	for(i=1; i<=m; i++) {
		int act,x,y;
		act=in();x=in();y=in();
		switch(act) {
			case 0: 
			case 1: 
			case 2:{
				int fx=gf(x),fy=gf(y);
				if(fx==fy)break;
				dis[fx]=dis[x]^dis[y]^act;
				f[fx]=fy;
				break;
			}
			case 3:
			case 4: {
				int fx=gf(x),fy=gf(y);
				if(fx!=fy){
					printf("0\n");
					break;
				}
				int d1=dis[x],d2=dis[y];
				if(d2<d1)swap(d1,d2);
				if(d1==d2){
					printf("1\n");
					break;
				}
//				if(act==4){
//					printf("0\n");
//					break;
//				}
				if(d1==(d2^3)){
					printf("1\n");
				}
				else{
					printf("0\n");
				}
				break;
			}
		}
	}
}

这个玩意非常之卡常……我加了40行才跑过去。

T2 金色丝线将瞬间一分为二

题目描述:

题目私货实在是太多了,就给简化版题面(

依次给出 \(n\) 个点,求给出第几个点时,每个点之间的曼哈顿距离和大于 \(k\)

\(A,B\) 间曼哈顿距离为\(|X_A-X_B|+|Y_A-Y_B|\)

输入格式:

第一行两个数,\(n,k\)

第二至第\(n+1\)行:每行两个数,表示当前给出的点的横纵坐标。

输出格式:

共一行。表示第几个点开始每个点之间的曼哈顿距离和大于 \(k\)。若不存在则输出\(-1\)

样例数据:

输入:

5 10
1 1
2 2
3 3
4 4
5 5

输出:

4

数据范围:

\(n\leq6\times10^5,0\leq k\leq10^{18},0\leq X,Y\leq10^9\)


**非常之卡常。 **

常数不够优秀无法通过此题。

解法:

很容易想到此题解法:维护两个树状数组,分别记录已经加入的点的\(X\)坐标与\(Y\)坐标。每次取出坐标大于/小于当前点的点的\(X/Y\)坐标的和以及点的个数分别计算。

要点:

  • 线段树被卡了。

  • 需要离散化。

    • map离散化被卡了。

    • lower_bound(包括upper_bound)被卡了。

    • unique+sort也被卡了。

    • 需要使用结构体排序。

  • 每次加入点时如果调用树状数组次数超过8次大概率被卡。

  • 记得输出-1。

  • 最好使用fread。

讲完了。


KONO TLE代码哒!

//两个树状数组
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC target("avx")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")
using namespace std;
char *P1,*P2,buf[1<<20];
typedef unsigned long long ll;
#define GC (P1==P2&&(P1=buf,P2=buf+fread(buf,1,1<<20,stdin),P1==P2)?0:(*(P1++)))
//#define GC getchar()
inline ll in()
{
    ll ans;
    char t,k;
    while(((t=GC)!='-'&&(t>'9'||t<'0')));
    k=(t=='-');
    ans=k?0:(t-'0');
    while((t=GC)>='0'&&t<='9')ans=ans*10+t-'0';
    return k?-ans:ans;
}

inline int lowbit(int x)
{
	return x&(-x);
}
const int maxn=1200010;
ll n,d;
ll s1[maxn],s2[maxn],n1[maxn],n2[maxn];
ll x[maxn],y[maxn];
int p1[maxn],p2[maxn];
void ins(ll x,ll k,ll s[])
{
	for(;x<=n;x+=lowbit(x))s[x]+=k;
}
ll gs(ll x,ll s[])
{
	ll res=0;
	for(;x;x-=lowbit(x)){
		res+=s[x];
	}
	return res;
}
struct node{
	ll val;
	int pos;
}x1[maxn],y1[maxn];
bool cmp(node a,node b)
{
	return a.val<b.val;
}
int main()
{
	n=in();d=in();
	register ll i,j;
	int cntx=0,cnty=0;
	for(i=1;i!=n+1;++i){
		x[i]=in();y[i]=in();
		x1[i].val=x[i];
		y1[i].val=y[i];
		x1[i].pos=y1[i].pos=i;
	}
	sort(x1+1,x1+n+1,cmp);
	sort(y1+1,y1+n+1,cmp);
	for(i=1;i!=n+1;++i)
	{
		if(x1[i].val!=x1[i-1].val)cntx+=1;
		p1[x1[i].pos]=cntx;
		if(y1[i].val!=y1[i-1].val)cnty+=1;
		p2[y1[i].pos]=cnty;
	}
	ll res=0;
	for(i=1;i!=n+1;++i)
	{
		ll r1=gs(p1[i],s1);
		ll r2=gs(n,s1);
		ll r3=gs(p1[i],n1);
		ll r4=gs(n,n1);
		res+=x[i]*r3-r1;
		res+=(r2-r1)-x[i]*(r4-r3);
		ins(p1[i],x[i],s1);
		ins(p1[i],1,n1);
		r1=gs(p2[i],s2);
		r2=gs(n,s2);
		r3=gs(p2[i],n2);
		r4=gs(n,n2);
		res+=y[i]*r3-r1;
		res+=(r2-r1)-y[i]*(r4-r3);
		ins(p2[i],y[i],s2);
		ins(p2[i],1,n2);
		if(res>=d){
			printf("%llu",i);
			return 0;
		}
	}
	cout<<-1;
}

最后一个点被卡掉。原因是树状数组调用次数过多。

T3 神在夏至祭降下了神谕

题目难度非递增的典例(捶地

题目描述

夏至祭是一场迎接祖灵于夏季归来同时祈求丰收的庆典。村里的男人会在广场上演出冬之军跟夏之军的战争,夏之军会打倒冬之军的大将冬男,再放火将他连山车一起烧掉。

谢尔吉斯村长已经选好了 \(N\) 个人参加演出,其中一些人负责演夏之军,另一些人负责演冬之军。由于人数众多,谢尔吉斯想把这 \(N\) 个人分成若干个连续的段。为了保证演出的顺利进行,每段的夏之军人数与冬之军人数之差的绝对值不能超过 \(K\)

谢尔吉斯想知道符合条件的划分方案有多少种。由于符合条件的方案有很多, 你只要输出方案数除以 \(10^9+7\) 的余数。

输入格式

第一行两个数 \(N,K\)

第二行 \(N\) 个数字,第 \(i\) 个数字为0表示第\(i\)人是夏之军,否则表示第\(i\)人是冬之军。

输出格式

一行一个整数,表示符合条件的方案数除以 \(10^9+7\) 的余数。

样例数据

输入:

4 1
0 0 1 1

输出:

5

解释:

合法的5种方案分别为:

0 0 1 1

0 0 1|1

0|0 1 1

0|0 1|1

0|0|1|1

而 0 0|1|1 不是合法的方案,

因为第一段“00”中夏之军人数为2 ,冬之军人数为0,人数之差的绝对值超过了K 。

数据范围:

\(1\leq N\leq10^5,0\leq K\leq N\)


好水!真是好水!(指长江发洪水

解法:

由于题目中的限制为0,1两个数的个数差,于是我们尝试建立一个个数差的前缀数组。即$sum[i]-sum[j-1]=$0,1的个数差。

然后就能发现这题跟T2有些类似,都有统计某个区间内合法数字个数的操作。于是开一个树状数组,以个数差为横轴,方案数为权值。从1到n每次统计个数差在合法范围内的方案数。注意如果\(1\)~\(i\)区间也满足条件记得把方案数加上1。

代码简单易懂,也很简短。

空间需要开大,4倍空间能保证不RE(因为需要把负数个数差转成正数下标)


KONO 代码哒!

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;
inline int lowbit(int x)
{
	return x&(-x);
}
const int maxn=200010;
const ll mod=1e9+7;
ll sum[maxn<<3];
ll n,k;
void ins(int x,ll k)
{
	for(;x<(maxn<<3);x+=lowbit(x))
	sum[x]=(sum[x]+k)%mod;
}
ll gs(int x)
{
	ll res=0;
	for(;x;x-=lowbit(x))
	res=(res+sum[x])%mod;
	return res;
}
ll s[maxn];
int a[maxn];
ll f[maxn];
int main()
{
	cin>>n>>k;
	int i,j;
	s[0]=400000;
	for(i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		if(a[i]){
			s[i]=1;
		}
		else s[i]=-1;
		s[i]+=s[i-1];
	}
	ll ans=0;
	ins(s[0],1);
	for(i=1;i<=n;i++)
	{
		f[i]=0;
		f[i]=(f[i]+(gs(s[i]+k)-gs(s[i]-k-1)+mod)%mod)%mod;
		ins(s[i],f[i]);
	}
	cout<<f[n];
}
posted @ 2020-08-21 22:14  国土战略局特工  阅读(270)  评论(0)    收藏  举报