暑假集训六题居然只过了俩, 难受呀,别说鑫爹我自己都对自己有点失望。
I self-criticism three thousand ! ! ! !
I self-criticism three thousand ! ! ! !
A. The Door Problem
题目描述:n扇门,0表示关着,1表示开着,一扇门有两把钥匙,给m把钥匙以及他能打开的门,判断是否能同时把所有的门打开。
思路:
2-SAT裸题,鑫爹赛后一提2-SAT 就突然醒悟,全部都通了,对于每把钥匙区分一个0,1,状态,0是不选,1,是选,然后对于门的情况建边,如果这门开着那么要么一个也不选要么一起选,也就是说加入两把钥匙是a,b那么建边,a->b,b->a, a->!b,b->!a
如果门关着,那么只能选一个也就是说,a->!b ,b->!a , !b->a,!a->b。真弱啊,这么明显都没看出来。
I self-criticism three thousand ! ! ! !
I self-criticism three thousand ! ! ! !
I self-criticism three thousand ! ! ! !
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,m;
const int MAXN = 2e5+5;
const int MAXM = 4e5+5;
vector <int>v[MAXN];
int a[MAXN];
int dfn[MAXN],tot,low[MAXN],belong[MAXN],head[MAXN],cnt,col;
bool Instack[MAXN];
struct edge{
int to,next;
}Edge[MAXM];
stack<int>S;
void add(int x,int y)
{
Edge[++tot].to=y;Edge[tot].next=head[x];head[x]=tot;
}
void tarjan(int x)
{
dfn[x]=low[x]=++cnt;
S.push(x);
Instack[x]=1;
for(int i=head[x];i!=-1;i=Edge[i].next){
int to=Edge[i].to;
if(!dfn[to]){
tarjan(to);
low[x]=min(low[to],low[x]);
}
else if(Instack[to]){
low[x]=min(low[x],dfn[to]);
}
}
if(dfn[x]==low[x]){
Instack[x]=0;
belong[x]=++col;
while(S.top()!=x){
belong[S.top()]=col;
Instack[S.top()]=0;
S.pop();
}
S.pop();
}
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
for(int i=0;i<m;i++){
int x;
scanf("%d",&x);
for(int j=1;j<=x;j++){
int r;
scanf("%d",&r);
v[r].push_back(i);
}
}
for(int i=1;i<=n;i++){
int aa=v[i][0],bb=v[i][1];
if(a[i]==1){
add(2*aa,2*bb);
add(2*bb,2*aa);
add(2*aa+1,2*bb+1);
add(2*bb+1,2*aa+1);
}
else{
add(2*aa,2*bb+1);
add(2*bb,2*aa+1);
add(2*bb+1,2*aa);
add(2*aa+1,2*bb);
}
}
while(S.size())S.pop();
for(int i=0;i<m;i++){
if(!dfn[2*i])tarjan(2*i);
if(!dfn[2*i+1])tarjan(2*i+1);
}
bool flag=1;
for(int i=0;i<m;i++){
if(belong[2*i]==belong[2*i+1]){
flag=0;break;
}
}
if(flag)printf("YES\n");
else printf("NO\n");
}
B. Okabe and Boxes
题目描述:
有一个栈,然后add id 就是把id这个数字扔进去,然后remove就是出栈,但是必须按照1-n出栈,如果满足不了可以自己重新排序栈内的数字,问最少多少次排序。
题目思路:
既然题目保证了remove的数字肯定存在,那么先查看栈首,如果栈首是想要的直接出栈,如果不是想要的,那么肯定需要排序,可是咋的个排序方法呢,我先把栈清空,意思就是我之前来的东西已经全部都排好序了,如果又来了一个remove询问,那么如果栈空,而且还保证想要的数字肯定有,那么肯定是在栈首,因为现在的栈虽然是空的但是下边都是有序的,可能要问如果刚开始就入栈一个十分大的数字x,那么remove怎么处理,x肯定要在(1~x-1)之后处理,那么前边的remove使用上边的方法,如果又来一群大的,那么肯定要这个往前排。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN = 3e5+5;
char C[2*MAXN][10];
int main()
{
deque<int>s;
int n;
cin>>n;
int now=1;
int ans=0;
for(int i=1;i<=2*n;i++){
cin>>C[i];
if(C[i][0]=='a'){
int val;
cin>>val;
s.push_back(val);
}
else {
if(!s.empty()){
if(s.back()==now){
s.pop_back();
}
else{
s.clear();
ans++;
}
}
now++;
}
}
cout<<ans<<endl;
}
C. Cards Sorting
题目大意:
n张卡片,每次从前往后找,找到当前最小的就拿出来,直到全部出来,问需要多少次取卡片。
题目思路:(对于我这个模拟黑洞来说还不如直接把我模拟死)
在数字范围上建立vector,存储数字下标,我们用一个ans存储现在要加的值,用sum处理最终结果,最小的卡片肯定需要n次,加到答案里之后,要把这个数的数量减去,第二小的是时候,要先找到之前最后编号的那个,然后找这个编号之后有多少个,然后找这个编号后边有多少张卡片,把这些减去,因为这些在第一轮已经删掉了,加入答案里之后,再把这个数字之前的数字个数删掉,成为下一次要加的个数。
题目的细节简直在拿刀捅我,下次查找的数字下标注意是本次找到的那个数字下标-1,如果没找到就初始化成本轮数字的最后一个下标,如果找到了但是是我这个数字的最开始的那个那么就要直接减去长度,然后更新最后下标为最后一个。
打这种模拟太菜了呀,尤其这种思维凌乱的花里胡哨的。
I self-criticism three thousand ! ! !!! ! ! ! ! ! !
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll MAXN = 1e5+5;
vector<int>v[MAXN];
ll a[MAXN];
int main()
{
ll n;
cin>>n;
ll Max=-1;
for(ll i=1;i<=n;i++){
cin>>a[i];
Max=max(Max,a[i]);
v[a[i]].push_back(i);
}
ll bef=0;
ll ans=n,sum=0,index=n+1,now;
for(ll i=1;i<=Max;i++){
ll len=v[i].size();
now=len;
if(!len)continue;
bool f=0;
for(int j=0;j<len;j++){
if(v[i][j]>index){
now=j;f=1;
index=v[i][j];break;
}
}
if(!f)index=v[i][len-1];
if(now==0){
ans-=len;
index=v[i][len-1];
continue;
}
index=v[i][now-1];
ans-=len-now;
sum+=ans;
ans-=now;
}
cout<<sum<<endl;
}
D. Igor and his way to work
bfs最小转弯,但是更简单的方法是处理拐弯点,看两边是否可达。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
char C[1005][1005];
int vis[1005][1005];
int sx,xy,ex,ey,n,m;
bool check(int x,int y)
{
if(x>=1&&x<=n&&y>=1&&y<=m&&C[x][y]!='*')return 1;
else return 0;
}
int dx[4]={0,0,-1,1};
int dy[4]={-1,1,0,0};
struct node{
int x,y,cnt,dic;
node(){}
node(int a,int b,int c,int d){
x=a,y=b,cnt=c;dic=d;
}
bool operator <(const node &r)const{
return cnt>r.cnt;
}
};
bool bfs(int sx,int sy,int ex,int ey)
{
queue<node>q;
while(!q.empty())q.pop();
node ss(sx,sy,0,-1);
q.push(ss);
while(!q.empty())
{
node now=q.front();
q.pop();
//cout<<now.x<<" "<<now.y<<" "<<" "<<now.dic<<now.cnt<<endl;
int xx,yy,ccnt,ddic;
for(int i=0;i<4;i++){
xx=now.x+dx[i];
yy=now.y+dy[i];
//cout<<xx<<" * "<<yy<<" * "<<now.dic<<endl;
ccnt=now.cnt;
ddic=i;
if(now.dic==0&&ddic==1)continue;
if(now.dic==1&&ddic==0)continue;
if(now.dic==2&&ddic==3)continue;
if(now.dic==3&&ddic==2)continue;
if( (now.dic==i) ||now.dic==-1){
}
else ccnt++;
//cout<<xx<<" + "<<yy<<endl;
if(ccnt>2)continue;
if(check(xx,yy)&&ccnt<=vis[xx][yy]){
if(xx==ex&&yy==ey){
vis[xx][yy]=ccnt;
}
else {
q.push(node(xx,yy,ccnt,i));
vis[xx][yy]=ccnt;
}
}
if(vis[ex][ey]!=0x3f3f3f3f)break;
}
if(vis[ex][ey]!=0x3f3f3f3f)break;
}
// cout<<vis[ex][ey]<<endl;
if(vis[ex][ey]<=2)return 1;
else return 0;
}
int main()
{
int sx,sy,ex,ey;
scanf("%d%d",&n,&m);
for(int i=0;i<=n+1;i++){
for(int j=0;j<=m+1;j++){
vis[i][j]=0x3f3f3f3f;
}
}
for(int i=1;i<=n;i++){
scanf("%s",C[i]+1);
for(int j=1;j<=m;j++){
if(C[i][j]=='S'){
sx=i,sy=j;
}
if(C[i][j]=='T'){
ex=i,ey=j;
}
}
}
if(bfs(sx,sy,ex,ey))
printf("YES\n");
else
printf("NO\n");
}
E. Sorting the Coins
题目思路:维护离n最近的那个0,可以暴力,但是复杂度是卡过去的,我们可以建立树状数组然后二分,如果当前点到n的区间和小于区间长度就false,反之true。
暴力做法(966ms):
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN = 3e5+5;
int num[MAXN];
int main()
{
int n;
scanf("%d",&n);
int index=n;
ll ans=0;
printf("1");
for(int i=1;i<=n;i++){
int a;
cin>>a;
num[a]=1;
while(num[index]!=0){
index--;
}
//cout<<endl<<index<<"++"<<endl;
ans=i-(n-index);
printf(" %d",ans+1);
}
}
二分做法(677ms):
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=3e5+5;
int a[MAXN];
int num[MAXN],c[MAXN];
int n;
int ask(int x){
int ans=0;
for(;x;x-= x&-x)ans+=c[x];
return ans;
}
void add(int x,int y)
{
for(;x<MAXN;x+=x&-x)c[x]+=y;
}
bool check(int x)
{
ll now=ask(n)-ask(x-1);
if(now==n-x+1)return 1;
else return 0;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
add(a[i],1);
ll now=ask(n);
int l=1,r=n,ans=-1;
while(l<=r)
{
int mid=(l+r)/2;
//cout<<mid<<"----"<<endl;
if(check(mid)){
r=mid-1;
ans=mid;
}
else l=mid+1;
}
//cout<<ans<<endl;
if(ans==-1){
num[i]=i;
}
else num[i]=i-(n-ans+1);
//cout<<endl;
}
for(int i=0;i<=n;i++){
cout<<num[i]+1<<" ";
}
cout<<endl;
}
E.拓展域并查集裸题。题目都没看到,I self-criticism three thousand ! ! !! !
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=1e5+5;
int fa[3*MAXN];
int Find(int x)
{
if(x==fa[x])return x;
return fa[x]=Find(fa[x]);
}
void add(int x,int y){
if(Find(x)!=Find(y))
fa[Find(x)]=Find(y);
}
int main()
{
map<string,int>ss;
int n,m,q,cnt=0;
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=2*n;i++){
fa[i]=i;
}
for(int i=1;i<=n;i++){
string s;
cin>>s;
if(!ss[s])ss[s]=++cnt;
}
for(int i=1;i<=m;i++){
int op;
string s1,s2;
scanf("%d",&op);
cin>>s1>>s2;
int a=ss[s1],b=ss[s2];
if(op==1){
if(Find(a+n)==Find(b)||Find(b+n)==Find(a)){
printf("NO\n");
}
else add(a,b),add(a+n,b+n),printf("YES\n");
}
else{
if(Find(a)==Find(b)||Find(a+n)==Find(b+n)){
printf("NO\n");
}
else add(a+n,b),add(a,b+n),printf("YES\n");
}
}
for(int i=1;i<=q;i++){
string s1,s2;
cin>>s1>>s2;
int a=ss[s1],b=ss[s2];
if(Find(a)==Find(b)){
printf("1\n");
}
else if(Find(a+n)==Find(b)){
printf("2\n");
}
else printf("3\n");
}
}
唉怎么最近这么垃圾呀,头疼啊。
I self -criticism three thousand! !!!!!