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];
}

浙公网安备 33010602011771号