B Dragon slayer
题解
签到题。墙的数量只有15,爆搜或者状压都能比较轻松的通过。
标程
#include<bits/stdc++.h>
using namespace std;
int n,m,K,sx,sy,tx,ty;
struct edge{
int x[2],y[2];
}e[20];
int mp[20][20];
int Lw[20][20],Dw[20][20];
bool dfs(int x,int y){
mp[x][y]=1;
if(x==tx && y==ty){
return 1;
}
bool re=0;
//x-1 y
if(x>=1 && Lw[x][y]==0 && mp[x-1][y]==0){
re|=dfs(x-1,y);
}
//x+1 y
if(x+1<n && Lw[x+1][y]==0 && mp[x+1][y]==0){
re|=dfs(x+1,y);
}
//x y-1
if(y>=1 && Dw[x][y]==0 && mp[x][y-1]==0){
re|=dfs(x,y-1);
}
//x y+1
if(y+1<m && Dw[x][y+1]==0 && mp[x][y+1]==0){
re|=dfs(x,y+1);
}
return re;
}
bool check(int s){
memset(mp,0,sizeof(mp));
memset(Lw,0,sizeof(Lw));
memset(Dw,0,sizeof(Dw));
for(int i=0;i<K;++i){
if((s&(1<<i))==0){
if(e[i].x[0]==e[i].x[1]){
for(int j=e[i].y[0];j<e[i].y[1];++j){
Lw[e[i].x[0]][j]=1;
}
}
else{
for(int j=e[i].x[0];j<e[i].x[1];++j){
Dw[j][e[i].y[0]]=1;
}
}
}
}
return dfs(sx,sy);
}
int popcount(int x){
int re=0;
while(x){
if(x&1) ++re;
x>>=1;
}
return re;
}
int f[(1<<15)+5];
void solve(){
cin>>n>>m>>K>>sx>>sy>>tx>>ty;
for(int i=0;i<K;++i){
cin>>e[i].x[0]>>e[i].y[0]>>e[i].x[1]>>e[i].y[1];
if(e[i].x[0]==e[i].x[1]&&e[i].y[0]>e[i].y[1]){
swap(e[i].y[0],e[i].y[1]);
}
if(e[i].y[0]==e[i].y[1]&&e[i].x[0]>e[i].x[1]){
swap(e[i].x[0],e[i].x[1]);
}
}
int ans=K;
for(int s=0;s<(1<<K);++s){
f[s]=0;
}
for(int s=0;s<(1<<K);++s){
if(f[s]){
continue;
}
f[s]=check(s);
if(f[s]){
ans=min(ans,popcount(s));
for(int j=0;j<K;++j){
f[s|(1<<j)]|=f[s];
}
}
}
cout<<ans<<endl;
return;
}
int main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
int t=1;cin>>t;
for(int i=1;i<=t;++i){
solve();
}
return 0;
}
C Backpack
题解
fi,j,k表示前i个物品,异或和为j,并且体积为k的方案是否存在。
fi,j实际上是fi-1,j和fi-1,j\oplus vi左移wi位后V,可以用bitset压位
标程
#include<bits/stdc++.h>
using namespace std;
int n,m,cnt;
bitset<1050> f[1050],g[1050];
void solve(){
cin>>n>>m;
for(int j=0;j<1024;++j){
f[j].reset();
}
f[0][0]=1;
int x,y;
int ans=-1;
for(int i=1;i<=n;++i){
cin>>x>>y;
for(int j=0;j<1024;++j){
g[j]=f[j];
g[j]<<=x;
}
for(int j=0;j<1024;++j){
f[j]|=g[j^y];
}
}
for(int j=0;j<1024;++j){
if(f[j][m]){
ans=j;
}
}
cout<<ans<<endl;
return;
}
int main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
int t=1;cin>>t;
for(int i=1;i<=t;++i){
solve();
}
return 0;
}
D Ball
题解
显然我们需要判断出一个数是不是质数,由于范围只有2*10^5 ,各类筛都可以 接下来考虑枚举一下三个点,我们可以分别计算距离并进行判断 但这样子时间复杂度太高,考虑枚举两个点,如何确定第三个点,我们即要求其中一条边小于等于这条边,另一条边大于等于这条边,这可以利用两个bitset的&解决 处理最终答案的时候可以把边从小到大加入避免算重。
标程
#include<bits/stdc++.h>
#define M 2005
using namespace std;
int n,m,cnt,mark[400005];
long long ans;
struct pos{
int x,y;
}P[M];
struct hzl{
int s,a,b;
}A[M*M];
bool cmp(hzl a,hzl b){
return a.s<b.s;
}
bitset<2005>s[M];
int main(){
mark[1]=1;
for(int i=2;i<=200000;i++){
if(mark[i])continue;
for(int j=i+i;j<=200000;j+=i)mark[j]=1;
}
int T;
scanf("%d",&T);
while(T--){
ans=cnt=0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)s[i].reset();
for(int i=1;i<=n;i++)scanf("%d%d",&P[i].x,&P[i].y);
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
A[++cnt]=(hzl){abs(P[i].x-P[j].x)+abs(P[i].y-P[j].y),i,j};
sort(A+1,A+cnt+1,cmp);
for(int i=1;i<=cnt;i++){
if(!mark[A[i].s])ans+=(s[A[i].a]^s[A[i].b]).count();
s[A[i].a][A[i].b]=1;
s[A[i].b][A[i].a]=1;
}
printf("%lld\n",ans);
}
return 0;
}
F Travel Plan
题解
由于不存在长度为偶数的简单回路,所以任何两个简单回路不存在公共边。 一个不严谨的证明: 如果某两点之间存在三条不重合的简单路径,则三条路径中必然存在相同奇偶性的路径,就会存在偶数 长度的简单回路。 不存在偶数长度的简单回路,说明必然任意两点之间都不存在三条不重合的路径,即任意一条边只会存 在于一个简单回路上。 于是这个图就是仙人掌图。 求最大公因数为x 的路径,可以用莫比乌斯反演转化为求x的倍数的路径。 这样对只需要对n个只包含i的倍数的边的图计算简单路径数量。 仙人掌图上的简单路径数量可以用dp求得。
标程
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pb push_back
LL M=998244353;
const int N=1e5+10;
char ss[1<<24],*A=ss,*B=ss;
char gc()
{
return (A==B&&(B=(A=ss)+fread(ss,1,1<<20,stdin),A==B)?EOF:*A++);
}
int read()
{
int x=0;
char c;
while (c=gc(),c<48||c>57) ; x=c-48;
while (c=gc(),47<c&&c<58) x=x*10+c-48;
return x;
}
vector<int> factor[N];
void add(LL &x,LL y){
x+=y;
if(x>=M) x-=M;
}
bool is_pri[N+10];
LL pri[60000];
int mu[N+10];
int cntp=0;
void init_pri(){
mu[1]=1;
for(int i=2;i<=N;++i){
if(!is_pri[i]) {
pri[++cntp]=i;
mu[i]=-1;
}
for(int j=1;j<=cntp&&pri[j]*i<=N;++j){
is_pri[pri[j]*i]=1;
if(i%pri[j]==0) {
mu[pri[j]*i]=0;
break;
}
else{
mu[pri[j]*i]=-mu[i];
}
}
}
}
void init(){
for(int i=1;i<=100000;++i) factor[i].clear();
for(int i=1;i<=100000;++i){
for(int j=i;j<=100000;j+=i){
factor[j].pb(i);
}
}
init_pri();
}
int n,m,K;
int num,tp,cnt;
struct edge{
int u,v;
};
bool Flag;
int Sum;
vector<edge> U[N];
vector<int> a[N],b[N+N];
int dfn[N],low[N],q[N],cf[N];
void Tarjan(int x,int fa){
int y;
dfn[x]=low[x]=++num;cf[x]=0;
q[++tp]=x;
for(int i=0;i<a[x].size();++i){
y=a[x][i];
if(!dfn[y]){
Tarjan(y,x);
cf[x]+=cf[y];
low[x]=min(low[x],low[y]);
if(low[y]==dfn[x]){
++cnt;b[cnt].clear();
for(int j=0;j!=y;){
b[cnt].push_back(q[tp]);
b[q[tp]].push_back(cnt);
j=q[tp];--tp;
}
b[cnt].push_back(x);
b[x].push_back(cnt);
// cout<<cnt<<endl;
// for(int j=0;j<b[cnt].size();++j) cout<<b[cnt][j].r<<" ";
// cout<<endl;
}
}
else if(dfn[y]<dfn[fa]){
low[x]=min(low[x],dfn[y]);
cf[x]++;
cf[y]--;
}
else{
low[x]=min(low[x],dfn[y]);
}
}
if(cf[x]>1) Flag=1;
return;
}
LL F[N+N];
LL ans[N];
void DP(int x,int fa,int op){
int y,z,l=b[x].size();
for(int i=0;i<l;++i){
y=b[x][i];
if(y==fa) continue;
DP(y,x,op);
}
if(x<=n){
F[x]=1;
for(int i=0;i<l;++i){
y=b[x][i];
if(y==fa) continue;
add(ans[op],F[y]*F[x]%M);
add(F[x],F[y]);
}
}
else if(l==2){
F[x]=0;
for(int i=0;i<l;++i){
y=b[x][i];
if(y==fa) continue;
add(F[x],F[y]);
}
}
else{
F[x]=0;
for(int i=0;i<l;++i){
y=b[x][i];
if(y==fa) continue;
add(ans[op],F[y]*F[x]%M);
add(F[x],(F[y]+F[y])%M);
}
}
return;
}
void solve(){
int u,v,w;
n=read();m=read();K=read();
for(int i=1;i<=K;++i){
U[i].clear();
ans[i]=0;
}
for(int i=1;i<=m;++i){
u=read();v=read();w=read();
for(auto j:factor[w]){
U[j].pb((edge){u,v});
}
}
vector<int> que;
for(int i=1;i<=K;++i){
que.clear();
for(auto ed:U[i]){
a[ed.u].clear();
a[ed.v].clear();
b[ed.u].clear();
b[ed.v].clear();
dfn[ed.u]=dfn[ed.v]=0;
que.pb(ed.u);
que.pb(ed.v);
}
for(auto ed:U[i]){
a[ed.u].pb(ed.v);
a[ed.v].pb(ed.u);
}
num=tp=0;cnt=n;
for(auto x:que){
if(!dfn[x]){
Tarjan(x,0);
if(Flag){
cout<<"wrong data"<<endl;
exit(0);
return;
}
DP(x,0,i);
}
}
}
for(int i=1;i<=K;++i){
for(int j=2;j*i<=K;++j){
ans[i]+=mu[j]*ans[i*j];
}
ans[i]=(ans[i]%M+M)%M;
}
// for(int i=1;i<=K;++i){
// cout<<ans[i]<<" ";
// }
// cout<<endl;
LL Ans=0;
for(int i=1;i<=K;++i){
Ans^=ans[i];
}
cout<<Ans<<endl;
return;
}
int main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
init();
int t=1;
// cin>>t;
t=read();
for(int i=1;i<=t;++i){
solve();
}
return 0;
}
K Random
题解
答案为(n-m)/2,求出2的逆元即可
标程
#include <bits/stdc++.h>
using namespace std;
const int mo=1e9+7;
int main()
{
ios::sync_with_stdio(false);
int gg=(mo+1)/2;
int T;
cin>>T;
while (T--)
{
int n,m;
cin>>n>>m;
cout<<1ll*(n-m)*gg%mo<<endl;
}
return 0;
}
L Alice and Bob
题解
将每个值为i的数字视为2^(n-i),那么Alice的胜利条件就是最终局面中能出现2^n。 Alice将数字分成两个集合,Bob将其中一个集合减一,就相当于将这个集合中的数全部乘2,然后将另一 个集合删去。 如果Alice能将集合中的数字按照值二等分,那么无论Bob怎么操作,黑板上所有数字的总和实际是不变 的。 如果集合中的数字总和超过2^n,由于所有数字都是不超过2^n的2的幂次,那么Alice的每次分割总能使得两边集合的值均不小于2^(n-1)。 因此直接判断所有数字的2的幂次的总和即可。
标程
#include<bits/stdc++.h>
using namespace std;
int n;
int a[1000005];
void solve(){
cin>>n;
for(int i=0;i<=n;++i){
cin>>a[i];
}
for(int i=n;i>0;--i){
a[i-1]+=a[i]/2;
}
if(a[0]) cout<<"Alice\n";
else cout<<"Bob\n";
}
int main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
int t=1;cin>>t;
for(int i=1;i<=t;++i){
solve();
}
return 0;
}