A. Telephone Number
题意:随便删看是否能找到开头为8且长度为11的串
思路:贪心,从前面极限开删找到第一个8的位置后,算一下能否满足长度>=11,感觉我比赛的时候讨论复杂了,算了不管了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<cmath>
#define ll long long
using namespace std;
char s[110];
int main()
{
int t;
cin>>t;
while(t--)
{
int n;
scanf("%d",&n);
scanf("%s",s+1);
int flag=0;
for(int i=1;i<=n;++i)
{
if(s[i]=='8')
{
flag=i;
break;
}
}
if(n<11)
{
puts("NO");
continue;
}
else if(n==11)
{
if(s[1]=='8')
{
puts("YES");
continue;
}
else {
puts("NO");
continue;
}
}
else if(n>11)
{
if(!flag)
{
puts("NO");
continue;
}
else
{
if(n-flag+1<11)
{
puts("NO");
continue;
}
else
{
puts("YES");
continue;
}
}
}
}
return 0;
}
B.Lost Numbers
题解:
较水的交互题,直接1、2,2,3,…判断四次,然后全排列一下6!可秒
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<cmath>
#define ll long long
using namespace std;
int a[10]={4,8,15,16,23,42},d[5];
int main()
{
for(int i=1;i<=4;++i)
{
printf("? %d %d\n",i,i+1);
fflush(stdout);
scanf("%d",&d[i]);
}
do{
if(a[1]*a[0]==d[1]&&a[2]*a[1]==d[2]&&a[3]*a[2]==d[3]&&a[3]*a[4]==d[4])
break;
}while(next_permutation(a,a+6));
printf("!");
for(int i=0;i<6;++i)
printf(" %d",a[i]);
fflush(stdout);
return 0;
}
C. News Distribution
题解:并查集模板,没啥好说的
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<cmath>
#define ll long long
using namespace std;
const int maxn=5e5+10;
int fa[maxn],size[maxn];
int find(int x)
{
if(x==fa[x])return x;
return fa[x]=find(fa[x]);
}
void merge(int x,int y)
{
x=find(x);
y=find(y);
if(x==y)return;
if(size[x]<size[y])
swap(x,y);
fa[y]=x;
size[x]+=size[y];
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
{
fa[i]=i;
size[i]=1;
}
int k,a,b;
for(int i=1;i<=m;++i)
{
scanf("%d",&k);
if(!k)continue;
scanf("%d",&a);
for(int i=1;i<k;++i)
{
scanf("%d",&b);
merge(a,b);
}
}
for(int i=1;i<=n;++i)
printf("%d ",size[find(i)]);
return 0;
}
D. Bicolored RBS
题解:贪心,为了保证深度最小,每次遇到一个左括号就分别分配,遇到一次右括号就分配给最近的左括号
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<cmath>
#define ll long long
using namespace std;
const int maxn=2e5+10;
char s[maxn];
int main()
{
int n;
scanf("%d",&n);
scanf("%s",s);
int flag=0;
int len=strlen(s);
for(int i=0;i<len;++i)
{
if(s[i]=='(')
{
flag++;
cout<<flag%2;
}
else{
cout<<flag%2;
flag--;
}
}
return 0;
}
E. Range Deleting
题意:多少个f(l,r)使得删去数列中[l,r]的所有数后满足不下降序列
题解:debug了一小时,首先想到暴力枚举,即f(1,x1),f(2,x2),xi表示当前满足的最小的,容易发现,如果f(1,x1)也满足,则有单调性,f(1,x1+1)…f(1,x)也满足
所以总共有(x-x1+1)+(x-x2+1)…
那么怎么迅速求出xi呢
维护四个数组,l[i]表示i的第一个位置,r[i]表示i的最后一个位置,l1[i]表示[i,n]中数字在最前面的位置,r[i]表示[1,n]中数字最后一个位置
所以先用dp思想预处理所有数组,然后找到最小的x1,再用x1递推出剩余的xi,怎么递推呢,就是先在满足[1,i-1]不下降的情况下,再慢慢把x1往后递推直到也满足[1,i-1]与[x1+1,n]组合后不下降,也是dp思想
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<cmath>
#define ll long long
using namespace std;
const int maxn=1e6+10;
int n,x,l[maxn],r[maxn],l1[maxn],r1[maxn],a[maxn];
//[i,n]最前面的pos [1,i]最后的位置
int main()
{
scanf("%d%d",&n,&x);
for(int i=1;i<=n;++i)
scanf("%d",&a[i]);
memset(l,0x3f,sizeof(l));
for(int i=1;i<=n;++i)
{
l[a[i]]=min(i,l[a[i]]);
r[a[i]]=i;
}
memset(l1,0x3f,sizeof(l1));
for(int i=x;i>=1;--i)
l1[i]=min(l1[i+1],l[i]); //dp预处理
for(int i=1;i<=x;++i)
r1[i]=max(r1[i-1],r[i]);
int m=x;
//[m,x]非递减
for(;m>1&&r[m]<=l1[m+1];--m);//找最小的x1
ll ans=x-m+1;
//[1,i-1],[m+1,x]满足
for(int i=2;i<=x&&r1[i-2]<=l[i-1];++i)//[1,i-1]要满足
{
for(;m<i||r1[i-1]>l1[m+1];++m);//一直递推直到第一个m
ans+=x-m+1;
}
printf("%lld\n",ans);
return 0;
}
F. Scalar Queries
题解:
看了很久才看懂的一道题,首先不难化简到 f(l,r)为a[i]*他在[l,r]中的区间第几小
**面对这种从区间到点的问题,如果很难考虑,应转化为从点转换到区间,考虑每个点的贡献,**首先,每个点的贡献是来自于其左边和右边某个比它小的数aj,
ai与aj,同时包含他们的每个区间会使a[i]*sum[i]中的sum[i]+1,这样就成功把区间排序改变,变成统计该数左边和右边每个比他小的数所贡献的所有区间总数,引用一下他人的话就是,
所以我们可以用树状数组+离散化来统计,树状数组下标是维护的是排序序号,维护小于该序号的数字的区间总数前缀和,但是要注意要加上本身的区间,因为我们统计的只是小于,本身要+1,如果没有小于他的数,他的序号也是1,还是要+1,这里利用树状数组在时间上的插入保证了统计的一定是在a[i]前面的数 树状数组和线段树的这个性质常考
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<vector>
#include<cmath>
#include<map>
#define ll long long
using namespace std;
const ll mod=1e9+7;
const int maxn=5e5+10;
map<int,int>mp;
ll c[maxn],sum[maxn];
int n,a[maxn],b[maxn];
void add(int x,int y)
{
for(;x<=n;x+=x&-x)c[x]+=y;
}
ll ask(int x)
{
ll ans=0;
for(;x;x-=x&-x)ans+=c[x];
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+1+n);//离散化
for(int i=1;i<=n;++i)
mp[b[i]]=i;
for(int i=1;i<=n;++i)
{
sum[i]=ask(mp[a[i]])*(n-i+1)%mod; //前面比他小的
add(mp[a[i]],i); //左边区间个数
}
for(int i=1;i<=n;++i)
c[i]=0;
for(int i=n;i>=1;--i)
{
sum[i]=(sum[i]+ask(mp[a[i]])*i%mod)%mod; //右边比他小
add(mp[a[i]],n-i+1);
sum[i]=(sum[i]+(ll)(n-i+1)*i%mod)%mod;//还要统计自己d的
}
ll ans=0;
for(int i=1;i<=n;++i)
ans=(ans+(ll)a[i]*sum[i]%mod)%mod;
printf("%lld\n",ans);
return 0;
}