^ & ^(位运算+思维)
比赛链接:https://acm.dingbacode.com/showproblem.php?pid=6702
题目大意
给定整数A,B
,请求出C
的最小值使得(A xor C)&(B xor C)
的值最小。
若当C==0
时(A xor C)&(B xor C)==0
,请输出1
。
思路
(A xor C)&(B xor C)=(A & B) xor C
,再根据异或的特点可以知道 当C=A&B
时(A xor C)&(B xor C)=0
,即得到最小值。
别忘了特判输出 1 1 1的情况。
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+100;
int main()
{
int t;
cin>>t;
while(t--)
{
ll a,b;
cin>>a>>b;
ll c=a&b;
if(c==0) cout<<"1"<<endl;
else cout<<c<<endl;
}
return 0;
}
Shuffle Card(思维)
比赛链接:https://acm.dingbacode.com/showproblem.php?pid=6707
题目大意
给定
n
n
n张卡片,每张卡片上都有一个数字
a
i
a_i
ai,
1
<
=
a
i
<
=
n
1<=a_i<=n
1<=ai<=n,并且每一张卡片上的数字互不相同。一开始所有的卡片按照数字大小的顺序由小到大排列在桌上。
接下来会进行
m
m
m次操作,每次操作会把卡片
x
x
x(值为
x
x
x的卡片)移至序列的首位。
请输出 m m m次操作之后卡片的顺序。
思路
每一次都需要把卡片
x
x
x移动到首位,那就意味着在卡片
x
x
x前面的所有卡片都要向后退一位。
硬模拟的话太麻烦,有超时的风险,代码量还大,懒狗表示十分不情愿。
反向一想,为什么我们要让在卡片
x
x
x前面的所有卡片后退呢?为什么来到首位的卡片
x
x
x一定要是原本的那张卡片
x
x
x呢?
不当谜语人了,其实每次操作的时候我们并不需要把卡片
x
x
x移动到首位,而是做一件等价的事取代这个操作:把一张新的值为x的卡片x'放在首位。
最后输出答案的时候,由于每张卡片只会出现一次,所以每一张卡片最后的位置就应该是它在这个序列中第一次出现的位置。
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+100;
bool vis[maxn];
int main()
{
deque<int> q;
int n,m,x;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>x;
q.push_back(x);
vis[i]=false;
}
for(int i=1;i<=m;i++)
{
cin>>x;
q.push_front(x);
}
while(n--){
while(vis[q.front()]){
q.pop_front();
}
cout<<q.front();
vis[q.front()]=true;
q.pop_front();
cout<<" ";
}
}
Windows Of CCPC(规律+思维)
比赛链接:https://acm.dingbacode.com/showproblem.php?pid=6708
题目大意
已知
n
=
=
k
n==k
n==k时,
A
A
A是一个由C
与P
拼凑而成的大小为
2
k
∗
2
k
2^k*2^k
2k∗2k的矩阵。
n
=
=
1
n==1
n==1时,矩阵
A
A
A为:
n
=
=
2
n==2
n==2时,矩阵
A
A
A为:
请求出
n
=
=
k
n==k
n==k时矩阵
A
A
A的样子。
思路
这种题就属于那种只要看对眼了就可以牵回AC区防止爆0的题目。
样例中给出了 n = = 3 n==3 n==3时矩阵A的值:
CCCCCCCC
PCPCPCPC
PPCCPPCC
CPPCCPPC
PPPPCCCC
CPCPPCPC
CCPPPPCC
PCCPCPPC
一开始博主并没有注意到有什么规律,疯狂薅头发。
紧接着,我顺着CCPC
的顺序看了一下
n
=
=
1
n==1
n==1时的矩阵A,然后用同样的方法看了一下
n
=
=
2
n==2
n==2时的矩阵
A
A
A,然后发现了一个东西:
我们把
n
=
=
2
n==2
n==2时的矩阵
A
A
A拆成四个模块之后,我们发现三个C
对应的模块的内容是相同的,而P
所对应的模块和C
对应的模块内容上是相反的。这样的话只要知道了矩阵
A
k
A_k
Ak红色模块的内容就可以推出其他模块的内容。
而根据题目中给出的所有矩阵
A
A
A我们可以发现,矩阵
A
k
A_k
Ak的红色模块的内容就是矩阵
A
k
−
1
A_{k-1}
Ak−1。
k k k的值最大为 10 10 10,所以我们直接预处理出矩阵 A 10 A_{10} A10的值,矩阵 A k A_k Ak一定是矩阵 A 10 A_{10} A10的子矩阵(见红色字体)。
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+100;
char s[2500][2500];
void init()
{
s[1][1]=s[1][2]=s[2][2]='C';
s[2][1]='P';
for(int i=2; i<=10; i++)
{
for(int j=1; j<=(1<<(i-1)); j++)
for(int k=1+(1<<(i-1)); k<=(1<<i); k++)
{
s[j][k]=s[j][k-(1<<(i-1))];
s[j+(1<<(i-1))][k]=s[j][k-(1<<(i-1))];
}
for(int j=1+(1<<(i-1)); j<=(1<<i); j++)
for(int k=1; k<=(1<<(i-1)); k++)
{
if(s[j-(1<<(i-1))][k]=='C')
s[j][k]='P';
else
s[j][k]='C';
}
}
}
int main()
{
ios::sync_with_stdio(false);
int t;
cin>>t;
init();
while(t--)
{
int n;
cin>>n;
for(int i=1; i<=(1<<n); i++)
{
for(int j=1; j<=(1<<n); j++)
cout<<s[i][j];
cout<<'\n';
}
}
}
Fishing Master(贪心+优先队列)
比赛链接:https://acm.dingbacode.com/showproblem.php?pid=6709
题目大意
卖鱼强正在钓鱼,他要把鱼塘里的鱼全部钓出来煮熟带回家当晚餐。
鱼塘里现在有
n
n
n条鱼,煮熟第
i
i
i条鱼的时间为
a
i
a_i
ai分钟。
卖鱼强抛出鱼钩之后等待
k
k
k分钟就会钓上一条鱼(
n
n
n条鱼中的任意一条,一定会上钩,除非鱼塘没有鱼了)。
卖鱼强在钓鱼的时候无法煮鱼,但在煮鱼的时候可以钓鱼。
请问:卖鱼强最少需要多少时间才能把鱼塘里所有的鱼煮熟带回家呢?
思路
有点像小学的时候老师讲过的煮茶问题:给出煮茶过程中每一步的内容与所需要的时间,煮茶的部分过程是可以同时进行的,求出喝到茶的最短时间
。
同理,对于这道题而言,我们肯定希望在煮鱼的时候钓尽可能多的鱼,让钓鱼所需要花费的额外时间全部融入在煮鱼的时间内。
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+100;
ll a[maxn];
int main()
{
ios::sync_with_stdio(false);
int t;
cin>>t;
while(t--)
{
ll m;
int n;
cin>>n>>m;
for(int i=1;i<=n;i++)
cin>>a[i];
sort(a+1,a+1+n,greater<int>());
priority_queue<ll> q;
ll num=1;
ll ans=m;
for(int i=1;i<=n;i++)
{
ans+=a[i];
num+=a[i]/m;
q.push(a[i]%m);
if(num<i){
ans+=m-q.top();
q.pop();
}
}
cout<<ans<<'\n';
}
}
path(BFS+离线处理)
比赛链接:https://acm.dingbacode.com/showproblem.php?pid=6705
题目大意
给出一张由
n
n
n个点,
m
m
m条边组成的带权有向图,然后会有
q
q
q次询问。
每次询问需要输出第
k
k
k小的路线权值和。
所谓路线权值和为某一条路线上的权值之和。
需要注意的是:在下图中,a→b→a也是合理的路线:
思路
关于离线处理,博主在Codeforces Round #751 (Div. 2)部分题解(A ~ C)的B题中进行了说明,不太懂的小伙伴可以先去做一下那道题,理解一下离线处理的概念。
这道题实际上是一道十分普通的广搜题:
- 出发点可以是任意一个有出边的点;
- 每次向下搜索时用的是自己所有的出边;
- 所有的边可以被使用无数次;
只不过我们每次需要找到当前权值最小的路线,记录它的值并优先根据这条路线推衍出之后的路线,所以要用 优先队列(priority_queue) 进行存储,辅助搜索。
仅是上面的部分还不足够我们AC,我们还需要再做一些处理。
- 定义四元组 ( u , v , w , p ) (u,v,w,p) (u,v,w,p),其意义为:点 v v v是点 u u u的第 p p p个连接点 ( u → v ) (u→v) (u→v),两者之间的边权值为 w w w;
- 定义 G [ m a x n ] G[maxn] G[maxn]数组, G [ i ] G[i] G[i]负责存储点 i i i的所有出边信息;
- 将所有的边按照权值大小关系由小到大排序;
vector<pair<ll,int> > G[maxn];
scanf("%d%d%d",&n,&m,&q);
for(int i=1; i<=m; i++)
{
int u,v;
ll w;
scanf("%d%d%lld",&u,&v,&w);
G[u].push_back(make_pair(w,v));
//方便排序,pair类型排序时会按照pair->first的值由小到大默认排序
}
for(int i=1; i<=n; i++)
sort(G[i].begin(),G[i].end());
接下来我们将所有的询问进行离线处理,并找出要查找的 k i k_i ki中最大的那个以确定广搜的程度。
int ans[maxn];
int maxx=-1;
for(int i=1; i<=q; i++)
{
scanf("%d",&ans[i]);
maxx=max(ans[i],maxx);
}
最后我们说一下如果当前四元组的信息是 ( u , v , w , p ) (u,v,w,p) (u,v,w,p),接下来的可能。
- 首先,我们考虑接下来选的边是点
v
v
v的一条出边。如果点
v
v
v具有出边,那我们就要选它权值最小的那条出边,也就是
G
[
v
]
[
0
]
G[v][0]
G[v][0]。
那么此时状态就由 ( u , v , w , p ) (u,v,w,p) (u,v,w,p)变成了 ( v , G [ v ] [ 0 ] . s e c o n d , w + G [ v ] [ 0 ] . f i r s t , 0 ) (v,G[v][0].second,w+G[v][0].first,0) (v,G[v][0].second,w+G[v][0].first,0)。 - 其次,我们考虑接下来选的边是点
u
u
u的另一条出边。如果当前
u
→
v
u→v
u→v不是点u的全部出边中权值最大的那条边,那么我们就可以选比这条出边权值要大的当前最优解—
G
[
u
]
[
p
+
1
]
G[u][p+1]
G[u][p+1]。
那么此时状态就由 ( u , v , w , p ) (u,v,w,p) (u,v,w,p)变成了 ( u , G [ u ] [ p + 1 ] . s e c o n d , w + G [ u ] [ p + 1 ] . f i r s t − G [ u ] [ p ] . f i r s t , p + 1 ) (u,G[u][p+1].second,w+G[u][p+1].first-G[u][p].first,p+1) (u,G[u][p+1].second,w+G[u][p+1].first−G[u][p].first,p+1)。
( u → v u→v u→v与 u → G [ u ] [ p + 1 ] . s e c o n d u→G[u][p+1].second u→G[u][p+1].second是互斥的)
然后就没有然后了,就可以AC了。
(四元组灵感来源于大佬博客https://www.cnblogs.com/19992147orz/p/11405833.html)
AC代码
//cin会T掉,关闭输入流不知道可不可以
//博主直接就scanf了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=5e4+100;
ll value[maxn];
int ans[maxn];
struct node
{
ll w;
int u,v,p;
node(){}
node(int uu,int vv,ll ww,int pp):u(uu),v(vv),w(ww),p(pp){}
bool operator<(const node&a)const{
return a.w<w;
}
};
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,m,q;
vector<pair<ll,int> > G[maxn];
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=m;i++)
{
int u,v;
ll w;
scanf("%d%d%lld",&u,&v,&w);
G[u].push_back(make_pair(w,v));
}
for(int i=1;i<=n;i++)
sort(G[i].begin(),G[i].end());
priority_queue<node> pq;
for(int i=1;i<=n;i++)
if(G[i].size()){
pq.push(node(i,G[i][0].second,G[i][0].first,0));
//cout<<i<<"→"<<G[i][0].second<<"="<<G[i][0].first<<endl;
}
int maxx=-1;
for(int i=1;i<=q;i++)
{
scanf("%d",&ans[i]);
maxx=max(ans[i],maxx);
}
int pos=1;
while(pos<=maxx)
{
node tmp=pq.top();
pq.pop();
int u=tmp.u;
int v=tmp.v;
int p=tmp.p;
ll w=tmp.w;
value[pos]=w;
//cout<<"value["<<pos<<"]=("<<u<<"→"<<v<<")"<<w<<endl;
if(G[v].size())
pq.push(node(v,G[v][0].second,w+G[v][0].first,0));
if(p!=G[u].size()-1)
pq.push(node(u,G[u][p+1].second,w+G[u][p+1].first-G[u][p].first,p+1));
pos++;
}
for(int i=1;i<=q;i++)
printf("%lld\n",value[ans[i]]);
//cout<<endl;
}
}
后话
感谢阅读,希望能对你产生一点用处。
以下台词取自《银魂》第95集:
(MADAO才是人生赢家)