A-
题目大意:给定一个序列,能够交换任意两个数的正负性,问能否构造出不递减
方法: 负数必须是连续出现在前段,正数连续出现在后段。即构造出的序列是先负数后正数。
代码:
#include<iostream>
using namespace std;
const int N=100010;
int p[N];
int main()
{
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
int t;cin>>t;
while(t--)
{
int n;
cin>>n;
int cntn=0;
for(int i=1;i<=n;i++)
{
int x;
cin>>x;
p[i]=abs(x);
if(x<0)cntn++;
}
bool flag=true;
for(int i=1;i<=n;i++)
{
if(cntn)
{
cntn--;
p[i]=-p[i];
}
if(i-1)
{
if(p[i-1]>p[i])
{
flag=false;
break;
}
}
}
if(flag)
{
cout<<"YES"<<endl;
}else
{
cout<<"NO"<<endl;
}
}
return 0;
}
B-
题目大意:给定一个小写字母序列,再给若干个特殊的字母,每次操作,可以将序列中特殊字母的前一个字母删除。问删到不能删的时候,操作了几次
方法: 对于序列中出现的第一个特殊字母,它的操作数是前面的字母数;
对于前有特殊字母的特殊字母,它的操作数是(1+特殊字母之间的字母数)
这个1是当删到特殊字母连续的时候,删一次就可以使开端只剩一个特殊字母。
然后将所有操作数取最大值
代码:
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
const int N=200010;
char a[N];
bool st[26];
int main()
{
int t;
cin>>t;
while(t--)
{
memset(st,false,sizeof st);
vector<int> ans;
int n;
cin>>n;
cin>>a+1;
int k;cin>>k;
for(int i=1;i<=k;i++)
{
char c;cin>>c;
st[c-'a']=true;
}
int cnt1=0;
int cnt2=0;
for(int i=1;i<=n;i++)
{
if(st[a[i]-'a'])
{
ans.push_back(cnt1+cnt2);
cnt2=1;
cnt1=0;
}else
{
cnt1++;
}
}
int res=0;
for(auto x:ans)
{
res=max(res,x);
}
cout<<res<<endl;
}
return 0;
}
C-
题目大意:给定2个全排列a,b。 再给一个需要构造的全排列c。
c[i]不是0:c[i]要么为a[i]要么为b[i],
c[i]是0,那么就可以任取a[i]或b[i]。
现在问能构造符合条件的全排列c的数字。
方法: 对于确定值的c[i],不用考虑。不确定的就话,如果可以使对应所有的a[i],b[i]成一个圈,那么就有两种情况,即答案乘2。
注意:如果对于任选的a[i],b[i](c!=0)在确定的a[i],b[i](c==0)出现过,说明不能任选,那么就构不成一个圈。
代码:
#include<iostream>
#include<cstring>
#include<map>
using namespace std;
const int N=100010;
const int mod=1e9+7;
typedef pair<int,int> PII;
int a[N],b[N];
int p[N];
bool st[N];
bool v[N];
int main()
{
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(0);
int t;cin>>t;
while(t--)
{
memset(v,false,sizeof v);
memset(st,false,sizeof st);
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
for(int i=1;i<=n;i++)
{
cin>>b[i];
}
for(int i=1;i<=n;i++)
{
int c;
cin>>c;
if(c)
{
st[a[i]]=true;
st[b[i]]=true;
}
}
for(int i=1;i<=n;i++)
{
p[a[i]]=b[i];
}
long long res=1;
for(int i=1;i<=n;i++)
{
if(!v[i])
{
int j=i;
int cnt=0;
bool flag=false;
while(!v[j])
{
v[j]=true;
flag|=st[j];
j=p[j];
cnt++;
}
if(cnt!=1&&!flag)
{
res*=2;
res%=mod;
}
}
}
cout<<res<<endl;
}
return 0;
}
D-
题目大意:给定一个无限大的六边形网格区域(蜂窝吗),每次可以画一条直线。
直线有三种情况:与x轴平行,夹60°,夹120°
这些直线相互之间和网格之间可以形成等边三角形。
现给定得到的三角形数量,问最少画了多少根直线
方法: 规律:每次加一根直线,最多可以增加另外两种直线数量和的二倍。
预处理出直线数所能构造出的最多三角形数。
然后二分。
代码:
代码1:找到规律后发现每一轮(加三根不同直线)会多产生12个三角形。
#include<iostream>
using namespace std;
int main()
{
long long res=0;
long long temp=0;
for(int i=0;res<=1e9;i++)
{
cout<<i<<" "<<3*i<<" "<<res<<endl;
temp+=12;
res+=temp;
}
//12909 38727 999853686
return 0;
}
代码2:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int N=40000;
int res[N];
int main()
{
int t;cin>>t;
res[1]=0;res[2]=2;res[3]=6;
int cnt[3]={1,1,1};
for(int i=4;i<=38730;i++)
{
int j=i%3;
int temp=0;
for(int k=0;k<3;k++)
{
if(k!=j)
{
temp+=cnt[k]*2;
}
}
cnt[j]++;
res[i]=res[i-1]+temp;
}
while(t--)
{
int n;cin>>n;
int l=1,r=38730;
while(l<r)
{
int mid=l+r>>1;
if(res[mid]>=n)
{
r=mid;
}else
{
l=mid+1;
}
}
cout<<r<<endl;
}
return 0;
}
E-
题目大意:一颗有n个点的树,边和点都有权值,是[1,2n-1]的全排列,找到一个根节点,使从根节点开始最大路径值最小。
方法: 参考于https://zhuanlan.zhihu.com/p/510465004
讲的挺好的,是大佬%%%%