大意:
给你一个序列,你要判断能否找到两个不同的子序列使得他们或起来相等。题目会给出每个数二进制下哪些位为 1,保证输入量不超过 1e5。
思路:
显然,如果某一些数对应的序列是另一些数的子序列,就肯定可以。我们不妨直接找最大的序列,然后对于每一个数检查一下它在这个序列里是否有独有的贡献。如果没有,就可以构造。
code
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=1e5+10;
ll n;
vector<ll> vt[N];
map<ll,ll> mp;
void solve()
{
mp.clear();
cin>>n;
for(int i=1;i<=n;++i) vt[i].clear();
for(int i=1;i<=n;++i)
{
ll m;
cin>>m;
for(int j=1;j<=m;++j)
{
ll a;
cin>>a;
vt[i].push_back(a);
mp[a]++;
}
}
for(int i=1;i<=n;++i)
{
bool fl=1;
for(auto j:vt[i])
{
if(mp[j]==1) fl=0;
}
if(fl)
{
cout<<"Yes"<<endl;
return;
}
}
cout<<"No"<<endl;
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
ll t;cin>>t;while(t--)
solve();
return 0;
}
大意:
给你两个数 n,x,求最小的 m 使得 n&(n+1)&(n+2)&…&m=x。无解输出-1
思路:
显然是要一位一位去check
我们假设在第i位,n对应的数字是a,b对应的数字是b
若a=0且b=1,显然无解,与操作不能无中生有
若a=1且b=0, 我们的m需要满足:第i位为0,且m>=n,这里我们就可以更新下限
若a=1且b=1, 则从n到m的每一个数字的第i位都要等于1,这里我们可以更新上限
那么最后看一下上下限是不是合法就可以了
code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=1e5+10;
ll n,m;
void solve()
{
cin>>n>>m;
ll l=n,r=5e18;
for(int i=0;i<60;++i)
{
ll a=(n>>i)&1;ll b=(m>>i)&1;
if(a==0&&b==1)
{
cout<<-1<<endl;
return;
}
ll tmp=((n/(1ll<<i)+1)<<i);
if(a==1&&b==0)
{
l=max(l,tmp);
continue;
}
if(a==1&&b==1)
{
r=min(r,tmp-1);
}
}
if(l<=r)
{
cout<<l<<endl;
}
else cout<<-1<<endl;
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
ll t;cin>>t;while(t--)
solve();
return 0;
}
大意:
给你 n个数,若 gcd(ai,aj)≠1则在 i,j之间连一条无向边,求从 s 到 t 的最短路。
思路:
思路很明显,就是对每一个点向自己的因子连边,然后跑最短路。优化一下,只连质因子。再优化一下,用一手欧拉筛,就好了。某人边数没算对,T烂了,我不说是谁
code
#include<bits/stdc++.h>
using namespace std;
#define ll int
#define endl '\n'
const ll N=3e5+10;
ll n,m;
ll A,B;
ll mas[N];
ll dis[N<<1];
ll vis[N<<1];
ll pre[N<<1];
vector<ll> ans;
struct ty
{
ll t,l,next;
}edge[10000005];
ll cn=0;
ll head[N<<1];
void add(ll a,ll b,ll c)
{
edge[++cn].t=b;
edge[cn].l=c;
edge[cn].next=head[a];
head[a]=cn;
}
void bfs()
{
// memset(dis,-1,sizeof dis);
deque<int> q;
q.push_back (A);
dis[A] = 1;
while (!q.empty ()) {
auto u = q.front ();
q.pop_front ();
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v = edge[i].t, dist = edge[i].l;
if (dis[v]) continue;
if (dist == 1) q.push_back (v); //1尾
else q.push_front (v); //0首
dis[v] = dis[u] + dist;
pre[v] = u; //记录前驱, 输出路径
}
}
}
void solve()
{
memset(head,-1,sizeof head);
cin>>n;
for(int i=1;i<=n;++i) cin>>mas[i];
cin>>A>>B;
for(int i=1;i<=n;++i)
{
for(int j=2;j*j<=mas[i];++j)
{
if(mas[i]%j) continue;
while(mas[i]%j==0) mas[i]/=j;
//cout<<i<<' '<<j<<endl;
add(i,n+j,0);
add(n+j,i,1);
}
if(mas[i]!=1)
{
//cout<<i<<" "<<mas[i]<<endl;
add(i,n+mas[i],0);
add(n+mas[i],i,1);
}
}
bfs();
if(dis[B]==0)
{
cout<<-1<<endl;
return;
}
cout<<dis[B]<<endl;
ll pos=B;
while(pos!=A)
{
ans.push_back(pos);
pos=pre[pos];
pos=pre[pos];
}
reverse(ans.begin(),ans.end());
cout<<A<<' ';
for(auto i:ans) cout<<i<<" ";
cout<<endl;
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
// ll t;cin>>t;while(t--)
solve();
return 0;
}
大意:
给你一个序列,你可以执行任意次以下操作:
选出一个子序列,将奇数位 +1,偶数位 −1,或将奇数位 −1,偶数位 +1。
问最少几次操作使得序列变成全 0
思路:
纯抽象,cf的思维题我一向是很认可的
显然我们会尽可能去扩展每一次的操作范围。对于一个正数来说,如果某一次操作能让他--,我们肯定会做,如果是让他++,我们肯定不会做,这是显然的,因为这里不会有比直接减更好的策略,毕竟我们每次操作改变的值是固定的。
然后,我们从前往后看。如果当前的数字a大于0,我们至少要a次--操作,那么我们就留给了后面的第一个数字a次++操作,同时也会消耗掉之前的数字留给我们的a次--操作。然后如果a小于0,我们需要消耗|a|次++操作,留给后面|a|次--操作。那么在这个过程中,如果需要的次数不够了,我们就直接加,因为这就是我们不得不额外做的操作。否则就用之前的操作传下来的就好了
是不是很玄学
code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N=3e5+10;
ll n,m;
ll mas[N];
void solve()
{
cin>>n;
for(int i=1;i<=n;++i) cin>>mas[i];
ll cn1=0,cn2=0;
for(int i=1;i<=n;++i)
{
if(mas[i]>0)
{
cn2+=mas[i];
cn1=max(0ll,cn1-mas[i]);
}
else if(mas[i]<0)
{
cn1-=mas[i];
cn2=max(0ll,cn2+mas[i]);
}
}
cout<<cn1+cn2<<endl;
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
ll t;cin>>t;while(t--)
solve();
return 0;
}