题解报告
基本的一些理解和问题都在注释中
A:Swap Odd and Even
//水题
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
string s;cin>>s;
int len=s.size();
for(int i=0;i*2<len;i++)
swap(s[2*i],s[2*i+1]);
cout<<s<<endl;
return 0;
}
//水题
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;
const int maxn=2e5+10;
int num[maxn];
int flag[maxn];
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int N;cin>>N;
for(int i=1;i<=N;i++)cin>>num[i];
for(int i=1;i<=N;i++)
if(!flag[i])flag[num[i]]=1;
int res=0;
for(int i=1;i<=N;i++)if(!flag[i])res++;
cout<<res<<endl;
for(int i=1;i<=N;i++)if(!flag[i])cout<<i<<' ';
cout<<endl;
return 0;
}
//水题
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <unordered_map>
using namespace std;
const int maxn=15;
int W[maxn][maxn];
int dirx[2]={0,1};
int diry[2]={1,0};
unordered_map<int,int> mp;
int N,M,res;
void dfs(int x,int y)
{
if(x==N&&y==M) {
++res;
}
else{
for(int i=0;i<2;i++)
{
int fx=dirx[i]+x;
int fy=diry[i]+y;
if(fx<=N&&fy<=M&&!mp[W[fx][fy]])
{
mp[W[fx][fy]]=1;
dfs(fx,fy);
mp[W[fx][fy]]=0;
}
}
}
return;
}
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>N>>M;
for(int i=1;i<=N;i++)
for(int j=1;j<=M;j++)
cin>>W[i][j];
mp[W[1][1]]=1;
dfs(1,1);
cout<<res<<endl;
return 0;
}
D:Tying Rope
题目大意: 把一些线连接起来,找联通和环。
我使用的是建图然后暴力搜索,好像并查集也可以做
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
using namespace std;
const int maxn=2e5+10;
int vis[maxn];
int vis2[maxn];//这个是里面看的。
struct edge
{
int R,B;
}Edge[maxn];
bool dfs(int u,int dir)//第二个记录哪边不能走
{
if(u==0)return false;
vis[u]=1;
vis2[u]=1;
bool res=false;
if(dir!=1)//蓝色的不能走
{
int Dir=0;
if(Edge[Edge[u].R].B==u) Dir=0;
else Dir=1;
if(vis2[Edge[u].R])return true;
else{
res|=dfs(Edge[u].R,Dir);
}
}
if(dir!=0)//红色的不能走
{
int Dir=0;
if(Edge[Edge[u].B].B==u)Dir=0;
else Dir=1;
if(vis2[Edge[u].B])return true;
else{
res|=dfs(Edge[u].B,Dir);
}
}
return res;
}
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int N,M;cin>>N>>M;
for(int i=0;i<M;i++)
{
char op1,op2;
int u,v;
cin>>u>>op1>>v>>op2;
if(op1=='R'){
if(op2=='R'){
Edge[u].R=v;
Edge[v].R=u;
}else{
Edge[u].R=v;
Edge[v].B=u;
}
}else{
if(op2=='R'){
Edge[u].B=v;
Edge[v].R=u;
}else{
Edge[u].B=v;
Edge[v].B=u;
}
}
}
int res1=0,res2=0;
for(int i=1;i<=N;i++)
{
if(!vis[i])
{
if(dfs(i,-1))res1++;//1代表红。
else res2++;
}
}
cout<<res1<<' '<<res2<<endl;
return 0;
}
E:Geometric Progression
题目大意: 求一个等比数列的和后取模。
等比数列有如下性质:
\(S_n=q*S_{n-1}+1\)
可以知道等比数列的前\(N\)项的和是可以通过递推式写出来的。可以考虑利用矩阵的乘法实现一步一步的递推
有如下形式:
取\(S_0\)=0,则有
\[\begin{pmatrix} S_n\\ 1\\ \end{pmatrix}=\begin{pmatrix} q&1\\ 0&1\\ \end{pmatrix}^n\begin{pmatrix} 0\\ 1\\ \end{pmatrix}\]从上面可以看出,利用矩阵的快速幂求取\(n\)次方后,获得的矩阵的右上角就是\(S_n\)。
如果这里使用等比数列的求和公式的话会有如下式子:
\(\sum_{i=0}^{n-1}q^i=\frac {q^n-1}{q-1}\),其中\(q-1\)是要用逆元来取模的,但是这道题目的模数不一定是质数,所以不一定能拿来用,所以不能用这种方法,要用矩阵快速幂。
//等比数列求和
//注意质数,这里的求乘法逆元的时候只有质数才能用来取模,不是质数就不可以。
//这道题的模数是不确定的,所以不能使用逆元。
//然后用矩阵运算的规则
//以后遇到等比数列求和且模不确定的数,就直接用矩阵快速幂。
//矩阵的快速幂适用于有递推公式的。
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#define ll long long
using namespace std;
void mul(ll A[2][2],ll B[2][2],int MOD)
{
ll temp[2][2];
memset(temp,0,sizeof(temp));
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
for(int k=0;k<2;k++)
temp[i][j]=(temp[i][j]+A[i][k]*B[k][j])%MOD;
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
A[i][j]=temp[i][j];
}
int M_fpow(ll A,ll B,int MOD)
{
ll res[2][2]{{1,0},{0,1}};
ll M[2][2]={{A,1},{0,1}};
while(B)
{
if(B&1)mul(res,M,MOD);
B>>=1;
mul(M,M,MOD);
}
return res[0][1];
}
//X次方取右上角
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
ll A,B,C;cin>>A>>B>>C;
cout<<M_fpow(A,B,C)<<endl;
}
F:Zero or One
(我刚开始没读懂题目,还是看了别人的题解才懂的,菜是原罪)
这道题目居然能用二分写,终究是我写的题目少了,想都想不到。
//二分的题目,用所有可能的状态和原来的状态进行匹配
//如果是看进制的话,就可以把一个数分成好几部分来进行判断,但是又不能分很多份
//所以要先处理好一些要分成很多区间的进制,最后剩下的就是比较大的进制,然后对所有的状态进行枚举判断就行了,看看后面的状态是否能有对应的进制来满足。
//这道题主要是根据不同的区间利用不同的算法进行判断。
#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#define ll long long
using namespace std;
bool Invalid(int bit,ll num)//先把一些小的进制枚举掉,那么就可以缩小最后需要枚举的状态的范围
{
while(num)
{
if(num%bit>1)return false;
num/=bit;
}
return true;
}
int check(ll bit,int state,ll num)//这里是最容易出错的地方,如果没有限制好就非常容易超出去
{
__int128 now=0,Now_bit=1;//防止两个乘起来后很大//用__int128
for(int i=0;i<7;i++)
{
if(state>>i&1)
{
if(Now_bit>num)return 1;//这里是联合下面一起来限制的,太大了就不行。
now+=Now_bit;
}
if(now>num)return 1;
if(Now_bit<2e18)Now_bit*=bit;//这个累乘法的是很大的比如说bit为1e18那么在只有最后一位是1的时候就有(1e18)^6非常大,要限制
}
if(now==num)return 0;
else return -1;
}
bool Has_bit(int state,ll num)//利用状态去找进制,看看能不能找到
{
ll l=1001,r=2e18;
//二分去找,有判断的条件,有大有小
while(l<r)
{
ll mid=(l+r)>>1;
if(check(mid,state,num)!=-1)r=mid;
else l=mid+1;
}
//最后还要判断下合不合法
return check(l,state,num)==0;
}
int main(void)
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int T;cin>>T;
while(T--)
{
ll N;cin>>N;
ll ans=0;
for(int i=2;i<=1000;i++)if(Invalid(i,N))ans++;//先处理前一千以内的进制。
for(int i=1;i<1<<7;i++)if(Has_bit(i,N))ans++;
cout<<ans<<endl;
}
return 0;
}
最后一题好像是板子题,但是我不会,哎~~(原因还是懒得学)
学了莫队再说吧。