9.28
T1 三元一次方程
题目大意
\(ax+by+cz=d\),若\(x,y,z\) 有整数解,输出\(YES\) 反之\(NO\)
分析
不妨令\(z=0\),那显然如果\(d\) % \(gcd(a,b)\) 为 \(0\) 那显然\(x,y\)有解
将其推广,显然 \(d\) % \(gcd(a,b,c)\) 为 \(0\) 时,答案有解
坑点 特判 \((a,b,c)\) 为 \(0\) 时 因为 \(gcd\) 进行取模操作时不能为 \(0\)
点击查看代码
#include<iostream>
#include<cstdio>
#include<cstring>
#define int long long
using namespace std;
int t,a,b,c,d,Gcd;
void init(int x){ if(x<0) x=-x; }
int gcd(int a,int b) {return b?gcd(b,a%b):a;}
signed main()
{
// freopen("niuniufx.in","r",stdin);
// freopen("niuniufx.out","w",stdout);
scanf("%lld",&t);
for(int i=1;i<=t;i++)
{
scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
if(d==0)
{
printf("YES\n");
continue;
}
if(a&&b&&c) Gcd=gcd(a,gcd(b,c));
if(!a&&b&&c) Gcd=gcd(b,c);
if(a&&!b&&c) Gcd=gcd(a,c);
if(a&&b&&!c) Gcd=gcd(a,b);
if(!a&&!b&&c) Gcd=c;
if(!a&&b&&!c) Gcd=b;
if(a&&!b&&!c) Gcd=a;
if(!a&&!b&&!c)
{
printf("NO\n");
continue;
};
if(!(d%Gcd)) printf("YES\n");
else printf("NO\n");
}
return 0;
}
T2 牛牛的猜球游戏
考智商题,初看时老老实实 \(swap\)
对于区间问题时,首先想到线段树,莫队,差分前缀
此题可以差分
举个例子
原序列 \(1,2,3,4\)
进行 \(i\) 操作后 变为 \(3,1,2,4\) 记为 \(l\)
那我们对于每个操作进行记录
用一个数组记录 \(i\) 次操作后
\(3\) 到 \(1\) 位置 \(\quad tmp[3]=1\)
\(1\) 到 \(2\) 位置 \(\quad tmp[1]=2\)
\(2\) 到 \(3\) 位置 \(\quad tmp[2]=3\)
\(4\) 到 \(4\) 位置 \(\quad tmp[4]=4\)
那么现在从小到大输出 \(tmp[i]\) 构造一个数列 \(2,3,1,4\)
这个数列进行 \(i\) 次操作后,惊奇的变为 \(1,2,3,4\) 抵消了前 \(i\) 次操作
那 \(r\) 序列 按照 \(tmp[l-1]\) 的 映射关系输出 就抵消了前 \(l\) 的操作
点击查看代码
#include<cstdio>
#include<iostream>
#define int long long
using namespace std;
int n,m;
int f[100010][11],l,r,hd,tl,a[11],tmp[11],now[11];
signed main()
{
// freopen("ball1.in","r",stdin);
// freopen("ball1.out","w",stdout);
scanf("%lld%lld",&n,&m);
for(int i=0;i<10;i++) f[0][i]=a[i]=i;
for(int i=1;i<=n;i++)
{
scanf("%lld%lld",&hd,&tl);
swap(a[hd],a[tl]);
for(int j=0;j<10;j++) f[i][j]=a[j];
}
while(m--)
{
scanf("%lld%lld",&l,&r);
for(int i=0;i<10;i++) tmp[f[l-1][i]]=i;// i-1的逆序列 即f[l-1][i] 在 i 位上
for(int i=0;i<10;i++) cout<<tmp[f[r][i]]<<" ";// 将f[r][] 照着原来f[l-1][]的逆序再排一遍 相当于抵消
cout<<endl;
}
return 0;
}
/*
5 3
0 1
1 2
2 3
0 1
9 0
1 5
5 5
3 5
*/
T3 牛牛的凑数游戏
跟神秘数 一模一样
说白了,做不来
今天初识主席树
从 \(1\) 枚举可行答案
在不同的版本树中加入更新值
在求解 \(l\quad r\) 时
枚举可行答案序列 \(ans\) ,相当于从父到子
如果到达答案区间,并且答案区间差值 \(x\) ( 该区间从\([1,x]\) 必然严格覆盖 )大于枚举的 \(ans\)
说明\(ans\)可以到达 \(x\) 下次从 \(x+1\) 开始枚举
如果 \(x\) 小于 \(ans\) 说明此时 \(ans\) 已经无法到达
建议野性理解
点击查看代码
#include<bits/stdc++.h>
#define maxn 6000010
#define int long long
using namespace std;
struct node{int val,lc,rc;}t[maxn];
int n,m,a[maxn],sum,root[maxn];
int l,r,tot,ans;
int upd(int pre,int l,int r,int p)
{
int x=++tot;
t[x]=t[pre];
t[x].val+=p;
if(l==r) return x;
int mid=(l+r)>>1;
if(p<=mid) t[x].lc=upd(t[pre].lc,l,mid,p);
else t[x].rc=upd(t[pre].rc,mid+1,r,p);
return x;
}
int ask(int L,int R,int l,int r,int ll,int rr)
{
if(ll<=l&&r<=rr) return t[R].val-t[L].val;
int mid=(l+r)>>1 , ans=0;
if(ll<=mid) ans+=ask(t[L].lc,t[R].lc,l,mid,ll,rr);
if(rr>mid) ans+=ask(t[L].rc,t[R].rc,mid+1,r,ll,rr);
return ans;
}
signed main()
{
scanf("%lld\n",&n);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
sum+=a[i];
}
// root[0]=build(1,sum);
for(int i=1;i<=n;i++) root[i]=upd(root[i-1],1,sum,a[i]);
scanf("%lld",&m);
for(int i=1;i<=m;i++)
{
scanf("%lld%lld",&l,&r);
ans=1;
while(1)
{
int tmp=ask(root[l-1],root[r],1,sum,1,ans);
// cout<<t[root[l-1]].val<<" "<<t[root[r]].val<<" st ";
if(tmp>=ans)
{
// cout<<tmp<<" * ";
ans=tmp+1;
}
else break;
}
printf("%lld\n",ans);
}
return 0;
}
/*
5
1 2 4 9 10
5
1 1
1 2
1 3
1 4
1 5
*/