文章目录
A. The Door Problem
题目链接
题意:有n扇门,每扇门都由两个开关控制,问能否通过变换使们全部打开。
思路:2-sat。如果门的初始状态为1,则控制它的两个开关,要么同为0,要么同为1。如果门的状态初始为0,则控制它的两个开关一个为0,另一个就为1。需要注意的就是如何建图。
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f;
typedef long long LL;
const int maxn=4e5+5;
struct Edge
{
int to,next;
} edge[maxn];
int head[maxn],tot,cnt,sat[maxn<<1],m,n;
bool mark[maxn<<1];
int a[maxn];
vector<int>v[maxn];
void init()
{
memset(head,-1,sizeof(head));
tot=cnt=0;
}
void add(int u,int valu,int v,int valv)///建图,为了方便通过异或操作来实现集合两个元素的转换
{
int uu=u*2+valu;
int vv=v*2+valv;
edge[tot]= {vv,head[uu]};
head[uu]=tot++;
edge[tot]= {uu,head[vv]};
head[vv]=tot++;
}
///2sat模板
bool dfs(int u)
{
if(mark[u^1])return 0;
if(mark[u])return 1;
mark[u]=1;
sat[cnt++]=u;
for(int i=head[u]; i!=-1; i=edge[i].next)
{
//cout<<i<<endl;
int v=edge[i].to;
if(!dfs(v))
{
return 0;
}
}
return 1;
}
bool twosat()
{
memset(mark,0,sizeof(mark));
for(int i=0; i<2*m; i+=2)
{
//cout<<n*2<<"**"<<mark[i]<<endl;
if(mark[i]||mark[i^1])continue;
cnt=0;
if(!dfs(i))
{
while(cnt)mark[sat[--cnt]]=0;
if(!dfs(i^1))return 0;
}
}
return 1;
}
int main()
{
//cout<<"***"<<endl;
while(~scanf("%d%d",&n,&m))
{
init();
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
}
for(int i=0; i<m; i++)
{
int k;
scanf("%d",&k);
for(int j=1; j<=k; j++)
{
int temp;
scanf("%d",&temp);
v[temp].push_back(i);
}
}
for(int i=1; i<=n; i++)
{
//cout<<v[i][0]<<","<<v[i][1]<<endl;
if(a[i]==0)
{
add(v[i][0],0,v[i][1],1);
add(v[i][0],1,v[i][1],0);
}
else
{
add(v[i][0],1,v[i][1],1);
add(v[i][0],0,v[i][1],0);
}
}
if(twosat())
{
printf("YES\n");
}
else printf("NO\n");
}
}
B. Okabe and Boxes
题目链接
题意:共有2n个操作,分为两种,第一种是向栈中加入一个数,第二种是移除栈顶数,要求移除的数按从1到n,你可以任意排列栈中的数,求满足题意的最小排列次数。
思路:用vector来模拟,如果栈顶不等于当前要删除的数,就把vector清空,ans++。
#include<bits/stdc++.h>
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
vector<int>v;
int main()
{
int n;
scanf("%d",&n);
int ans=0;
int temp=1;
for(int i=1; i<=2*n; i++)
{
char s[10];
scanf("%s",s);
if(s[0]=='a')
{
int x;
scanf("%d",&x);
v.push_back(x);
}
else
{
if(v.empty());
else if(v.back()==temp)
{
v.pop_back();
}
else
{
ans++;
v.clear();
}
temp++;
}
}
printf("%d\n",ans);
}
C. Cards Sorting
题目链接
题意:有一摞卡片,从最上面开始抽卡片,如果抽到的是当前最小的卡牌,就讲这张卡牌拿出来,否则就将这张卡牌放到牌堆底,问需要抽多少次。
思路:用vector将数值相同的位置存起来,将数组从小到大排序,用树状数组维护区间被删除的数。从小到大处理即可,唯一需要注意的就是可能有相同值的数。
#include<bits/stdc++.h>
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=1e5+5;
LL n,a[maxn],sum[maxn];
LL lowbit(LL x)///树状数组模板
{
return x&(-x);
}
void updata(LL x,LL d)
{
while(x<maxn)
{
sum[x]+=d;
x+=lowbit(x);
}
}
LL getsum(LL x)
{
LL res=0;
while(x>0)
{
res+=sum[x];
x-=lowbit(x);
}
return res;
}///树状数组模板
int main()
{
while(~scanf("%I64d",&n))
{
vector<LL>v[maxn];
for(LL i=1; i<=n; i++)
{
scanf("%I64d",&a[i]);
v[a[i]].push_back(i);
}
LL ans=0;
LL pre=0;
sort(a+1,a+1+n);
for(LL i=1; i<=n;)
{
LL pos=lower_bound(v[a[i]].begin(),v[a[i]].end(),pre)-v[a[i]].begin();
LL sz=v[a[i]].size();
if(pos==0)///如果当前全部最小的数的位置大于上一个最小的数,直接加上
{
ans+=v[a[i]][sz-1]-pre-(getsum(v[a[i]][sz-1])-getsum(pre));
pre=v[a[i]][sz-1];
}
else///如果不是全部位置比上一个数大,则需要将牌抽完,再开始新的一轮
{
ans+=n-pre-(getsum(n)-getsum(pre))+v[a[i]][pos-1]-getsum(v[a[i]][pos-1]);
pre=v[a[i]][pos-1];
}
for(LL j=0; j<sz; j++)
updata(v[a[i]][j],1);
i+=sz;
}
printf("%I64d\n",ans);
}
}
D. Igor and his way to work
题目链接
题意:从S走到T,不能走"*",问是否能在拐不超过两个弯的情况下走到。
思路:dfs,只要注意写法就好。
#include<bits/stdc++.h>
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=1005;
int n,m;
int vis[maxn][maxn][5];
char mp[maxn][maxn];
int dx[4]= {1,-1,0,0};
int dy[4]= {0,0,1,-1};
bool dfs(int x,int y,int dir,int t)
{
if(x<1||y<1||x>n||y>m||t>2)
return 0;
if(mp[x][y]=='T')
return 1;
if(mp[x][y]=='*')
return 0;
if(vis[x][y][dir]<=t)///如果走到过这个位置,并且用了更少的拐弯数,则直接return
return 0;
vis[x][y][dir]=t;
for(int i=0; i<4; i++)
{
int xx=x+dx[i];
int yy=y+dy[i];
if(dir==-1)///-1代表初始时可以任意选择方向
{
if(dfs(xx,yy,i,t))
return 1;
}
else if(i==dir)///方向没有改变
{
if(dfs(xx,yy,dir,t))
return 1;
}
else///方向发生改变,拐弯数+1
{
if(dfs(xx,yy,i,t+1))
return 1;
}
}
return 0;
}
int main()
{
memset(vis,INF,sizeof(vis));
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)
{
scanf("%s",mp[i]+1);
}
int x,y;
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
{
if(mp[i][j]=='S')
{
x=i,y=j;
}
}
}
if(dfs(x,y,-1,0))
{
printf("YES\n");
}
else
printf("NO\n");
}
E. Sorting the Coins
题目链接
题意:有一个最开始全都为’O’的序列,每次操作将其中一位变成’X’,然后从左往右开始扫描,如果遇到了X并且它的后面是O,则将他们两个交换,直到一次扫描发现没有可以交换的时候,结束。
思路:其实每一次就相当于将最右边的X移动到末尾。所以,就统计下标最大的右边有O的X前面有几个X即可。
#include<bits/stdc++.h>
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=3e5+5;
int a[maxn];
int pos[maxn];
int main()
{
int n;
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
}
printf("1");///最开始没有X,只扫一遍
int k=n,cnt=0;
for(int i=1; i<=n; i++)
{
pos[a[i]]++;
if(a[i]==k)
{
k--;
while(pos[k])
{
k--;
cnt++;
}
cnt++;
}
printf(" %d",i-cnt+1);
}
}
F. Mahmoud and a Dictionary
题目链接
题意:有n个词,每两个词之间有两种关系,一种是同义词,另一种是反义词,如果A与B是同义词,B与C是同义词,那么A与C也是同义词。如果A与B是反义词,B与C是反义词,那么A与C是同义词。给你m种关系,1 x y 代表x和y是同义词,2 x y代表x和y是反义词,问有多少个关系是矛盾的。有q次询问,问x和y是什么关系。
思路:带权并查集裸题。将每个单词用map映射一下,用0表示x与y同义,1表示x与y反义。如果A->B=1,B->C=1则A->C=0,所以值得更新是A->C=(A->B+B->C)%2。
#include<bits/stdc++.h>
#define LL long long
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=1e5+5;
int fa[maxn],rel[maxn],ans,m,q,n;
map<string,int>mp;
void init()
{
ans=0;
for(int i=0; i<maxn; i++)
fa[i]=i;
memset(rel,0,sizeof(rel));
}
int fin(int x)
{
if(x==fa[x])
return x;
int fx=fa[x];
fa[x]=fin(fa[x]);
rel[x]=(rel[x]+rel[fx])%2;
return fa[x];
}
bool join(int x,int y,int d)
{
int fx=fin(x);
int fy=fin(y);
if(fx==fy)
{
if(d!=(rel[x]-rel[y]+2)%2)
{
ans++;
return 1;
}
}
else
{
fa[fx]=fy;
rel[fx]=(rel[y]+d-rel[x])%2;
}
return 0;
}
int main()
{
while(~scanf("%d%d%d",&n,&m,&q))
{
init();
int cnt=0;
for(int i=1; i<=n; i++)
{
string s;
cin>>s;
mp[s]=++cnt;
}
for(int i=1; i<=m; i++)
{
int d;
string a,b;
cin>>d>>a>>b;
if(join(mp[a],mp[b],d-1))
{
printf("NO\n");
}
else
printf("YES\n");
}
for(int i=1; i<=q; i++)
{
string a,b;
cin>>a>>b;
int fx=fin(mp[a]);
int fy=fin(mp[b]);
if(fx!=fy)
{
printf("3\n");
}
else
printf("%d\n",((rel[mp[a]]-rel[mp[b]]+2)%2)+1);
}
}
}