I E比较复杂不适合单人比赛,总体来说题目难度还可以,但是很多简单题大家没发现。
A:显然期望为平均值的和,没有坑点。
B:坑点在于a+b可能暴int所以要用longlong存
C:其实会写的话这题可以很短,具体看代码(YE5和N0也是坑点)
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1000050;
int n,m;
int a[maxn];
int main()
{
char s1[1050],s2[1050];
int a,b;
cin>>a>>b>>s1;
sprintf(s2,"%d",a+b);
if(!strcmp(s1,s2)) cout<<"YE5"<<endl;
else cout<<"N0"<<endl;
return 0;
}
Sample Input
10
1101001010
Sample Output
-1
4
3
2
2
2
2
1
1
1
HINT
虽然霍大佬很强但是霍大佬的每种膜法只能释放一次(只能选择变换前就为1的位置)。即第1种膜法无法使1101001010依次变为1111001010,1111101010,1111111010,1111111110,111111111,该例子应输出-1。
题解:可能是题面写得太晦涩难懂了ORZ明明看懂题十分钟就差不多可以写完了。首先肯定要从第一个0的左边开始变(设第一个)第一个0位置是i)如果当前点是1判断下一个0在哪里就好,如果当前点是0就判断上一个1的位置从那继续。预处理每个点左边第一个1的位置和右边第一个10中1的位置就好,具体看代码:
#include<bits/stdc++.h>
using namespace std;
int a[100101];
int r[100010];
int l[100100];
int v[100100];
int main()
{
int n;long long ans=-1;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%1d",&a[i]);
}
a[n+1]=1;
int maxx=200001;
if(a[1]==0) {
for(int i=1;i<=n;i++)
printf("-1\n");
return 0;
}
for(int i=n;i>=0;i--)
{
if(a[i]==1&&a[i+1]==0){
maxx=i;
}
r[i]=maxx;
}
int minn=0;
for(int i=0;i<=n;i++)
{
if(a[i]==1){
minn=i;
}l[i]=minn;
}
long long money=0;
for(int len=1;len<=n;len++)
{
long long num=0;int nowpos=r[0];
while(nowpos<n){
num++;
if(a[nowpos]==1){
nowpos+=len;
if(nowpos>=n){
break;
}
if(a[nowpos]==1||(a[nowpos+1]==1)){
nowpos=r[nowpos];
}
else{
int tmp=nowpos;tmp=l[nowpos];
if(tmp+len<=nowpos){
num=-1;break;
}
else{
nowpos=tmp;
}
}
}
}
printf("%lld\n",num);
}
}
Sample Input
1
7x-100=2x+200
Sample Output
60
大模拟....坑点太多题面也不明白...不知道为什么这么多人绕过D写E QAQ
代码:
#include <cstdio>
#include <cstring>
using namespace std;
char s[400];
int xx, cx;
int rin(int a, int b) {
int ans = 0;
int f = 1;
if (s[a] == '-') {
f = -1;
a++;
}
else if (s[a] == '+') {
a++;
}
while (a < b && '0' <= s[a] && s[a] <= '9') {
ans = ans * 10 + s[a] - '0';
a++;
}
return ans * f;
}
void adin(int a, int b, int tp) {
int ss;
ss = rin(a, b);
if (s[b-1] == 'x') {
if (ss == 0) {
if (s[a] == '+' || s[a] == '-') {
if (s[a+1] == 'x')
ss = (s[a] == '+' ? 1 : -1);
}
else {
if (s[a] == 'x')
ss = 1;
}
}
xx += ss * tp;
}
else
cx += ss * tp;
}
void work() {
xx = 0;
cx = 0;
scanf("%s", s);
int len = strlen(s);
int a, b;
a = 0;
int tp = 1;
while (true) {
for (b = a + 1; b < len; b++) {
if (s[b] == '-' || s[b] == '+' || s[b] == '=')
break;
}
adin(a, b, tp);
if (s[b] == '\0')
break;
if (s[b] == '=') {
a = b + 1;
tp = -1;
}
else
a = b;
}
if (xx == 0 && cx != 0) {
printf("IMPOSSIBLE\n");
}
else if (xx == 0 && cx == 0) {
printf("IDENTITY\n");
}
else if (xx != 0 && cx == 0) {
printf("0\n");
}
else {
int ans = -cx/xx;
int ux, uc;
if (xx > 0)
ux = 1;
else
ux = -1;
if (cx > 0)
uc = 1;
else
uc = -1;
if (ux*(-uc) < 0) {
if (cx % xx != 0)
ans -= 1;
}
printf("%d\n", ans);
}
}
int main() {
int N;
scanf("%d", &N);
while (N--) {
work();
}
return 0;
}
Sample Input
5
0 2
2 1
2 3
4 5
3 6
Sample Output
5
HINT
样例中五个分别选择a,b,a,a,a即SG(x)={(0,2),(2,1),(2,3),(4,5),(3,6)}=5
题解:看似很难.....其实如果把两个数看成一条边的话,很容易想到如果有环存在那在与该环联通的所有点显然都可选,如果没有环存在,那么显然丢弃最大的点最优。
代码:
#include<bits/stdc++.h>
using namespace std;
int vis[100010];
int vv[100010];
int n;
int evis[200020];
vector<int>edg[100010];
int from[200020];int to[200020];
int dfs(int now){
vis[now]=vv[now]=1;int ans=now<<1;
for(int i=0;i<edg[now].size();i++){
if(!evis[edg[now][i]]){
evis[edg[now][i]^1]=evis[edg[now][i]]=1;
if(vis[to[edg[now][i]]]){
ans|=1;
}
else{
int tmp=dfs(to[edg[now][i]]);
ans=max(ans,tmp)|((tmp&1)|(ans&1));
}
}
}
return ans;
}
int main()
{
int nowe=0;
scanf("%d",&n);
for(int i=1;i<=n;i++){
int a,b;
scanf("%d%d",&a,&b);
edg[a].push_back(nowe);
from[nowe]=a;to[nowe]=b;nowe++;
edg[b].push_back(nowe);
from[nowe]=b;to[nowe]=a;nowe++;
}
for(int i=0;i<=n;i++){
if(!vis[i]){
int tmp=dfs(i);
if((tmp&1)==0){
vv[tmp>>1]=0;
}
}
}
for(int i=0;i<=n+1;i++){
if(!vv[i]){
printf("%d\n",i);return 0;
}
}
}
树上差分 记录每个节点的一级和二级差分 然后DFS一遍解决,验题人用的线段树解决二级差分,这里直接放两个代码:
树上二级差分代码(其实只有55行):
#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+5;
#define ll long long
#define pb push_back
vector<int>edg[maxn];
vector<long long>op[maxn];
vector<long long>de[maxn];
ll cha[maxn],ans[maxn];
ll cd[maxn];
int n,m;
void dfs(int u,int fa,int deep,long long sum,long long sd){
for (int i=0;i<op[u].size();i++){
cha[deep]+=op[u][i];
cd[deep]+=-1;
if (deep+de[u][i]<=n) {
cha[deep+de[u][i]+1]-=op[u][i]-de[u][i];
cd[deep+de[u][i]]-=-1;
}
}
ans[u]=sum+cha[deep]+sd;
for (int i=0;i<edg[u].size();i++){
if (edg[u][i]!=fa) dfs(edg[u][i],u,deep+1,sum+cha[deep]+sd,sd+cd[deep]);
}
for (int i=0;i<op[u].size();i++){
cha[deep]-=op[u][i];
cd[deep]-=-1;
if (deep+de[u][i]<=n) {
cha[deep+de[u][i]+1]+=op[u][i]-de[u][i];
cd[deep+de[u][i]]+=-1;
}
}
}
int main(){
scanf("%d",&n);
for (int i=0;i<n-1;i++){
int x,y;
scanf("%d%d",&x,&y);
edg[x].pb(y);
edg[y].pb(x);
}
scanf("%d",&m);
for (int i=0;i<m;i++){
int v;
ll x,d;
scanf("%d%lld%lld",&v,&d,&x);
op[v].push_back(x);
de[v].pb(min(d,x));
}
dfs(1,0,1,0,0);
for(int i=1;i<=n;i++){
printf("%lld\n", ans[i]);
}
}
线段树计算:
#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+5;
#define ll long long
#define pb push_back
struct OP{
int d;
ll x;
OP(int d,ll x):d(d),x(x){}
};
vector<int>G[maxn];
vector<OP>op[maxn];
int n,m;
ll ans[maxn];
ll b[maxn<<2],lazy[maxn<<2],N;
void build(int n){
N=1;
while (N<n+2) N<<=1;
}
void update(int L,int R,ll C){
ll s,t,Ln,Rn,x=1;
Ln=Rn=0;
for (s=N+L-1,t=N+R+1;s^t^1;s>>=1,t>>=1,x<<=1){
b[s]+=C*Ln;
b[t]+=C*Rn;
if (~s&1) lazy[s^1]+=C,b[s^1]+=C*x,Ln+=x;
if ( t&1) lazy[t^1]+=C,b[t^1]+=C*x,Rn+=x;
}
for (;s;s>>=1,t>>=1){
b[s]+=C*Ln;
b[t]+=C*Rn;
}
}
ll query(int L,int R){
ll s,t,Ln,Rn,x=1;
Ln=Rn=0;
ll ans=0;
for (s=N+L-1,t=N+R+1;s^t^1;s>>=1,t>>=1,x<<=1){
if (lazy[s]) ans+=lazy[s]*Ln;
if (lazy[t]) ans+=lazy[t]*Rn;
if (~s&1) ans+=b[s^1],Ln+=x;
if ( t&1) ans+=b[t^1],Rn+=x;
}
for (;s;s>>=1,t>>=1){
ans+=lazy[s]*Ln;
ans+=lazy[t]*Rn;
}
return ans;
}
void dfs(int u,int fa,int deep,ll sum){
for (OP &p:op[u]){
update(deep,deep,p.x);
if (deep+p.d<=n){
update(deep+p.d+1,deep+p.d+1,-p.x+p.d);
if (p.d>0) update(deep+1,deep+p.d,-1);
}else{
if (deep<n)update(deep+1,n,-1);
}
}
ans[u]=sum+query(deep,deep);
for (int &v:G[u]){
if (v!=fa) dfs(v,u,deep+1,sum+query(deep,deep));
}
for (OP &p:op[u]){ //回溯
update(deep,deep,-p.x);
if (deep+p.d<=n){
update(deep+p.d+1,deep+p.d+1,p.x-p.d);
if (p.d>0) update(deep+1,deep+p.d,1);
}else{
if (deep<n) update(deep+1,n,1);
}
}
}
int main(){
scanf("%d",&n);
build(n);
for (int i=0;i<n-1;i++){
int x,y;
scanf("%d%d",&x,&y);
G[x].pb(y);
G[y].pb(x);
}
scanf("%d",&m);
for (int i=0;i<m;i++){
int v,d;
ll x;
scanf("%d%d%lld",&v,&d,&x);
if (x<d) d=x;
op[v].push_back(OP(d,x));
}
dfs(1,0,1,0);
for(int i=1;i<=n;i++){
printf("%lld\n", ans[i]);
}
}
Sample Input
5 5 6
1 2 4 8 16
1 3
2 3
3 5
5 5
1 4
Sample Output
1
0
5
1
5
题解:离线,区间数量问题无法用线段树之类的解决所以只能莫队。维护一个异或前缀和的01字典树,新加入一个节点计算字典树中与该节点异或值大于k的数数量,就可O(logn)维护区间异或和大于k的数量,删除同理。
代码:
#include<bits/stdc++.h>
using namespace std;
int unit,arr[100005];
int utr=1;
long long ans,res[100050];
int k;
struct node{
int l,r,id;
}q[100050];
struct tree{
int next[2];
int num;
}tr[(1<<18)+5];
void init()
{
for(int i=1;i<(1<<17);i++){
tr[i].next[0]=i<<1;tr[i].next[1]=i<<1|1;tr[i].num=0;
}
}
void insert(int x,int v)
{
int now=1;
for(int i=16;i>=0;i--)
{
now=tr[now].next[(x>>i)&1];
tr[now].num+=v;
}
}
bool cmp(node a,node b){
return a.l/unit!=b.l/unit?a.l/unit<b.l/unit:a.r<b.r;
}
void add(int pos){
int now=1;int tmp=0;
for(int i=16;i>=0;i--)
{
if(((k>>i)&1)==0){
ans+=tr[tr[now].next[((k>>i)&1)^((arr[pos]>>i)&1)^1]].num;
}
now=tr[now].next[((k>>i)&1)^((arr[pos]>>i)&1)];
tmp=tmp|((((k>>i)&1)^((arr[pos]>>i)&1))<<i);
}
insert(arr[pos],1);
}
void remove(int pos){
int now=1;
for(int i=16;i>=0;i--)
{
if(((k>>i)&1)==0){
ans-=tr[tr[now].next[((k>>i)&1)^((arr[pos]>>i)&1)^1]].num;
}
now=tr[now].next[((k>>i)&1)^((arr[pos]>>i)&1)];
}
insert(arr[pos],-1);
}
int main(){
int n; int m;
init();
scanf("%d",&n); scanf("%d",&m);scanf("%d",&k);
unit=sqrt(n);
for(int i=1;i<=n;i++){
scanf("%d",&arr[i]);
arr[i]^=arr[i-1];
}
for(int i=1;i<=m;i++){
scanf("%d%d",&q[i].l,&q[i].r);
q[i].l--;
q[i].id=i;
}
sort(q+1,q+m+1,cmp);
int L=0,R=0;insert(0,1);
for(int i=1;i<=m;i++){
while(L>q[i].l)
{ L--;
add(L);
}
while(L<q[i].l)
{
remove(L);
L++;
}
while(R>q[i].r)
{
remove(R);
R--;
}
while(R<q[i].r)
{
R++;
add(R);
}
res[q[i].id]=ans;
}
for(int i=1;i<=m;i++){
printf("%lld\n",res[i]);
}
}
代码虽然有一百来行,但是60行是莫队模板,所以实际代码就三四十行(其实想得到的话写的还是应该挺快的)
Sample Input
29
Sample Output
543450786
HINT
三骨牌可以旋转,两种方案不同当且仅当这两种图案直接覆盖在一起无法重叠。
例如,当n=2时,有3种方案。
题解:其实推递推DP就很狗了,再抽成矩阵十进制快速幂就更狗了.....是队内大佬上个月出给noip巨佬学校du多校联考的题 题解移步三米诺,十进制快速幂
Sample Input
5 2
50 110 130 40 120
Sample Output
20
题解:
显然数字越大越容易满足条件,所以考虑二分答案。先将原数组排序设有序的新数组为a,二分答案为mid。用ok[i]代表第一个数到第i个数能否分为大小大于等于k的组(0表示不能,1表示能),并使组内最大值-最小值<=x。那么假设a[i]-a[now]<=x&&a[i]-a[now-1]>x。那么ok[now-1]到ok[i-k]中有任意一个值为1则ok[i]的值为1。判断ok[i]是否为1可以通过前缀和判断。而且显然now随着i的增长而增长,所以now可以用类似尺取的方式维护。所以总的复杂度就是O(nlogn)
代码:
#include <bits/stdc++.h>
#define maxn 300010
using namespace std;
int n, k, a[maxn], d[maxn];
int ok[300010];
bool check(int x) {
for(int i=1;i<=n;i++){
ok[i]=0;
}
ok[0]=1;int now=1;
for(int i=1;i<=n;i++){
while(a[i]-a[now]>x){
now++;
}
if(i-now+1>=k){
if(now==1||ok[i-k]-ok[now-2]>0){
ok[i]=1;
}
}
if(i!=n) ok[i]+=ok[i-1];
}
return ok[n];
}
int main() {
scanf("%d%d", &n, &k);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
sort(a+1, a+1+n);
a[0]=a[1];
int l = 0, r = a[n] - a[1];
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid+1;
}
printf("%d", l);
return 0;
}