CF1562
A The Miracle and the Sleeper
TP
考虑一个数,取模比他小的数模数的最大值一定是
(
a
+
1
)
/
2
(a+1)/2
(a+1)/2
同时
a
a
a要取得越大越好
考虑
l
l
l,如果
l
>
a
/
2
l>a/2
l>a/2 直接最大值为
r
%
l
r\%l
r%l
反之为
(
a
+
1
)
/
2
(a+1)/2
(a+1)/2
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e4+5;
int readint(){
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='-')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3)+(x<<1)+(s^48);
sc;
}
#undef sc
return x*f;
}
int main (){
int t=readint();
while(t--){
int a=readint(),b=readint();
if(a>b/2)
printf("%d\n",b%a);
else
printf("%d\n",((b+1)>>1)-1);
}
return 0;
}
B Scenes From a Memory
TP
考虑如果有
1
,
4
,
6
,
8
,
9
1,4,6,8,9
1,4,6,8,9直接输出就行了
反之只剩下
2
,
3
,
5
,
7
2,3,5,7
2,3,5,7,如果其中有一个的个数超过两个就直接输出
2
,
a
a
2,aa
2,aa
剩下就是大家都为一个,讨论情况易得
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e4+5;
int readint(){
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='-')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3)+(x<<1)+(s^48);
sc;
}
#undef sc
return x*f;
}
char s[55];
int cnt[10];
int main (){
int t=readint();
while(t--){
memset(cnt,0,sizeof(cnt));
int n=readint();
scanf("%s",s+1);
int flag=0;
for(int i=1;i<=n;i++){
int num=s[i]-'0';
cnt[num]++;
if(num==1||num==4||num==6||num==8||num==9){
printf("1\n%d\n",num);
flag=1;
break;
}
}
if(flag)
continue;
for(int i=1;i<=9;i++){
if(cnt[i]>=2){
printf("2\n%d%d\n",i,i);
flag=1;
break;
}
}
if(flag)
continue;
puts("2");
for(int i=1;i<=n;i++){
int num=s[i]-'0';
if(num==2){
cnt[2]--;
if(cnt[5])
puts("25"),flag=1;
else if(cnt[7])
puts("27"),flag=1;
}
if(num==3){
cnt[3]--;
if(cnt[5])
puts("35"),flag=1;
else if(cnt[2])
puts("32"),flag=1;
}
if(num==5){
cnt[5]--;
if(cnt[2])
puts("52"),flag=1;
else if(cnt[7])
puts("57"),flag=1;
}
if(num==7){
cnt[7]--;
if(cnt[5])
puts("75"),flag=1;
else if(cnt[2])
puts("72"),flag=1;
}
if(flag==1)
break;
}
}
return 0;
}
C Rings
TP
开头傻逼了,没想到前导零也算
那么有
0
0
0的时候直接取前面或者后面(看那一边大于
n
/
2
n/2
n/2即可)
如果没有
0
0
0,那就全是
1
1
1,取两个相等长度的即可
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e4+5;
int readint(){
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='-')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3)+(x<<1)+(s^48);
sc;
}
#undef sc
return x*f;
}
char s[maxn];
int main (){
int t=readint();
while(t--){
int n=readint();
scanf("%s",s+1);
bool flag=0;
for(int i=1;i<=n;i++){
if(s[i]=='0'){
flag=1;
if(i>n/2)
printf("1 %d 1 %d\n",i,i-1);
else
printf("%d %d %d %d\n",i,n,i+1,n);
break;
}
}
if(!flag){
printf("1 %d 2 %d\n",n-1,n);
}
}
return 0;
}
D Two Hundred Twenty One
TP
先考虑整个串,如果发现当前区间满足情况,直接输出
0
0
0
如果当前区间长度为奇数,那么只需要删除一个即可,因为贡献是逐渐累积的,设这个区间贡献为
s
s
s,你一定可以在一个位置找到他的前面的贡献是
s
/
2
s/2
s/2,后面为
s
/
2
s/2
s/2,
如果为偶数,先任意删一个位置,在按奇数的方法选择
接下来考虑删除位置,也就是考虑奇数长度删哪里,由上面所说,设删除的位置为
k
k
k,也就是
l
→
(
k
−
1
)
l\to (k-1)
l→(k−1)的贡献,等于
(
k
+
1
)
→
r
(k+1)\to r
(k+1)→r
也就是说
p
r
e
[
k
−
1
]
−
p
r
e
[
l
−
1
]
=
p
r
e
[
r
]
−
p
r
e
[
k
]
pre[k-1]-pre[l-1]=pre[r]-pre[k]
pre[k−1]−pre[l−1]=pre[r]−pre[k]
移项
p
r
e
[
k
−
1
]
+
p
r
e
[
k
]
=
p
r
e
[
l
−
1
]
+
p
r
e
[
r
]
pre[k-1]+pre[k]=pre[l-1]+pre[r]
pre[k−1]+pre[k]=pre[l−1]+pre[r]
然后先预处理,再去查找(离散化和大常数
S
T
L
STL
STL都可以)懒狗就是我
#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e5+5;
int readint(){
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='-')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3)+(x<<1)+(s^48);
sc;
}
#undef sc
return x*f;
}
char s[maxn];
int sum[maxn];
map<int,set<int> > mp;
int main (){
int t=readint();
while(t--){
int n=readint(),m=readint();
scanf("%s",s+1);
mp.clear();
sum[0]=0;
for(int i=1;i<=n;i++) {
sum[i]=sum[i-1]+((s[i]=='+')?1:-1)*((i&1)?1:-1);
mp[sum[i]+sum[i-1]].insert(i);
}
for(int i=1;i<=m;i++){
int x=readint(),y=readint();
if(sum[y]-sum[x-1]==0)
puts("0");
else{
if((y-x)&1){
puts("2"),printf("%d ",y);
y--;
}
else
puts("1");
int pos=*mp[sum[y]+sum[x-1]].lower_bound(x);
printf("%d\n",pos);
}
}
}
return 0;
}
E Rescue Niwen!
TP
易得,选了以
i
i
i号位开头的,那么全都选
像
a
b
c
⋅
⋅
⋅
⋅
⋅
⋅
abc······
abc⋅⋅⋅⋅⋅⋅,只选前三个,后面再遇到
a
b
c
abc
abc显然个数要少一些(感性理解,我并不知道末尾大小影响会造成啥)
然后求一个
L
C
P
LCP
LCP后再做
d
p
dp
dp即可
(我打死不会说我是用
S
A
SA
SA结果还是用
n
2
n^2
n2求的)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e3+5;
int readint(){
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='-')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3)+(x<<1)+(s^48);
sc;
}
#undef sc
return x*f;
}
char s[maxn];
int cnt[maxn],maxsize;
int sa[maxn],rk[maxn];
int x[maxn<<1],y[maxn<<1];
int lcp[maxn][maxn];
int height[maxn];
int dp[maxn];
void get_sa(int n){
int maxsize=150;
for(int i=0;i<=maxsize;i++)
cnt[i]=0;
for(int i=0;i<=n*2+5;i++)
x[i]=0,y[i]=0;
for(int i=1;i<=n;i++){
x[i]=s[i];
cnt[x[i]]++;
}
for(int i=1;i<=maxsize;i++)
cnt[i]+=cnt[i-1];
for(int i=n;i>=1;i--)
sa[cnt[x[i]]--]=i;
for(int k=1;k<=n;k<<=1){
int num=0;
for(int i=n-k+1;i<=n;i++)
y[++num]=i;
for(int i=1;i<=n;i++)
if(sa[i]>k)
y[++num]=sa[i]-k;
for(int i=1;i<=maxsize;i++)
cnt[i]=0;
for(int i=1;i<=n;i++)
++cnt[x[i]];
for(int i=1;i<=maxsize;i++)
cnt[i]+=cnt[i-1];
for(int i=n;i>=1;i--)
sa[cnt[x[y[i]]]--]=y[i],y[i]=0;
swap(x,y);
x[sa[1]]=1;num=1;
for(int i=2;i<=n;i++)
x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?num:++num;
if(num==n)
break;
maxsize=num;
}
}
void getheight(int n){
for(int i=1;i<=n;i++)
rk[sa[i]]=i;
// cout<<rk[2]<<" "<<rk[3]<<endl;
int k=0;
for(int i=1;i<=n;i++){
if(rk[i]==1)
continue;
if(k)k--;
int j=sa[rk[i]-1];
while(i+k<=n&&j+k<=n&&s[j+k]==s[i+k]) k++;
height[rk[i]]=k;
}
}
void getlcp(int n){
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
lcp[i][j]=0;
for(int i=1;i<=n;i++){
lcp[i][i]=n-sa[i]+1;
for(int j=i+1;j<=n;j++)
lcp[i][j]=min(lcp[i][j-1],height[j]);
}
for(int i=1;i<=n;i++)
for(int j=i-1;j;j--)
lcp[i][j]=lcp[j][i];
}
int main (){
int t=readint();
while(t--){
int n=readint();
scanf("%s",s+1);
get_sa(n);
getheight(n);
getlcp(n);
for(int i=1;i<=n;i++)
dp[i]=0;
dp[1]=n;
for(int i=2;i<=n;i++){
dp[i]=n-i+1;
for(int j=1;j<=i-1;j++){
if(rk[j]>rk[i]||lcp[rk[i]][rk[j]]==n-i+1)
continue;
dp[i]=max(dp[i],dp[j]+n-i+1-lcp[rk[i]][rk[j]]);
}
}
int maxx=0;
for(int i=1;i<=n;i++){
maxx=max(dp[i],maxx);
}
printf("%d\n",maxx);
}
return 0;
}
CF1567
A Domino Disaster
不做评价
#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e3+5;
int readint(){
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='-')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3)+(x<<1)+(s^48);
sc;
}
#undef sc
return x*f;
}
char s[105];
int main (){
int t=readint();
while(t--){
int n=readint();
scanf("%s",s+1);
for(int i=1;i<=n;i++){
if(s[i]=='U')
printf("D");
if(s[i]=='L')
printf("L");
if(s[i]=='R')
printf("R");
if(s[i]=='D')
printf("U");
}
puts("");
}
return 0;
}
B MEXor Mixup
TP
不做评价
#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e5+5;
int readint(){
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='-')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3)+(x<<1)+(s^48);
sc;
}
#undef sc
return x*f;
}
int pre[maxn];
int main (){
int t=readint();
for(int i=1;i<=3e5;i++)
pre[i]=pre[i-1]^i;
while(t--){
int a=readint(),b=readint();
int sum=pre[a-1],num=a;
if(sum==b)
printf("%d\n",num);
else if((b^sum)!=a)
printf("%d\n",num+1);
else
printf("%d\n",num+2);
}
return 0;
}
C Carrying Conundrum
TP
考虑到奇数位加到奇数位,偶数位加到偶数位,分开讨论即可
#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e5+5;
int readint(){
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='-')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3)+(x<<1)+(s^48);
sc;
}
#undef sc
return x*f;
}
int pre[maxn];
char s[15];
int main (){
int t=readint();
while(t--){
scanf("%s",s+1);
int n=strlen(s+1);
int len1=0,len2=0;
for(int i=1;i<=n;i++){
if(i&1)
len1=len1*10+(s[i]^48);
else
len2=len2*10+(s[i]^48);
}
printf("%d\n",(len2+1)*(len1+1)-2);
}
return 0;
}
D Expression Evaluation Error
TP
显然,保留最高位是最优秀的方案,借位保证个数先向低位借
#include <bits/stdc++.h>
using namespace std;
int readint(){
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='-')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3)+(x<<1)+(s^48);
sc;
}
#undef sc
return x*f;
}
int getlog10(int num){
int ans=1;
for(int i=1;;i++){
ans*=10;
if(ans>num)
return i-1;
}
}
int main (){
int t=readint();
while(t--){
int s=readint(),n=readint();
while(1){
if(n==1){
printf("%d\n",s);
break;
}
int num=pow(10,getlog10(s-n+1));
printf("%d ",num);
s-=num;
n--;
}
}
return 0;
}/
E Non-Decreasing Dilemma
TP
最开始的想法就是找出每段连续不下降的字串,但是查询和修改跟本解决不了,于是线段树,只需要注意中间拼接的问题,也就是
m
e
r
g
e
merge
merge,刚开始不写结构体
m
e
r
g
e
merge
merge不动,查询直接给我人干麻
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2e5+5;
int readint(){
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='-')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3)+(x<<1)+(s^48);
sc;
}
#undef sc
return x*f;
}
struct node{
int lx,rx,len;
int suml,sumr;
LL ans;
}tree[maxn<<2];
node merge(node x,node y){
node final;
final.lx=x.lx,final.rx=y.rx;
final.len=x.len+y.len;
final.ans=x.ans+y.ans;
if(x.rx<=y.lx){
final.ans+=1ll*x.sumr*y.suml;
if(x.suml==x.len)
final.suml=x.suml+y.suml;
else
final.suml=x.suml;
if(y.sumr==y.len)
final.sumr=y.sumr+x.sumr;
else
final.sumr=y.sumr;
}
else
final.suml=x.suml,final.sumr=y.sumr;
return final;
}
void build(int x,int l,int r){
if(l==r){
tree[x].lx=tree[x].rx=readint();
tree[x].len=tree[x].suml=tree[x].sumr=tree[x].ans=1;
return;
}
int mid=(l+r)>>1;
build(x<<1,l,mid);
build(x<<1|1,mid+1,r);
tree[x]=merge(tree[x<<1],tree[x<<1|1]);
}
void update(int x,int l,int r,int pos,int val){
if(l==r){
tree[x].lx=tree[x].rx=val;
return;
}
int mid=(l+r)>>1;
if(pos<=mid)
update(x<<1,l,mid,pos,val);
else
update(x<<1|1,mid+1,r,pos,val);
tree[x]=merge(tree[x<<1],tree[x<<1|1]);
}
node query(int x,int l,int r,int goal_l,int goal_r){
if(goal_l<=l&&goal_r>=r)
return tree[x];
int mid=(l+r)>>1;
if(goal_r<=mid)
return query(x<<1,l,mid,goal_l,goal_r);
else if(goal_l>mid)
return query(x<<1|1,mid+1,r,goal_l,goal_r);
else
return merge(query(x<<1,l,mid,goal_l,goal_r),query(x<<1|1,mid+1,r,goal_l,goal_r));
}
int main (){
int n=readint(),q=readint();
build(1,1,n);
while(q--){
int op=readint();
if(op==1){
int pos=readint(),val=readint();
update(1,1,n,pos,val);
}
else{
int l=readint(),r=readint();
printf("%lld\n",query(1,1,n,l,r).ans);
}
}
return 0;
}
CF1561
A Simply Strange Sort
TP
模拟题,复杂度不会算
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e3+5;
int readint(){
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='-')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3)+(x<<1)+(s^48);
sc;
}
#undef sc
return x*f;
}
int a[maxn];
int main(){
int t=readint();
while(t--){
int n=readint();
for(int i=1;i<=n;i++)
a[i]=readint();
int st=1,i=0;
while(1){
bool flag=1;
for(int j=1;j<=n;j++){
if(a[j]!=j)
flag=0;
}
if(flag){
printf("%d\n",i);
break;
}
st=i%2+1;
for(int j=st;j<n;j+=2){
if(a[j]>a[j+1])
swap(a[j],a[j+1]);
}
i++;
}
}
return 0;
}
B. Charmed by the Game
TP
先考虑最下情况,交替胜利且为保守球,后来多出来的单独排序,然后把前面的交替胜利一对一对互换,可以的到所有值,但是如果后面多出来的是奇数,那么要分类讨论哪一个人先发球
#include <bits/stdc++.h>
using namespace std;
int readint(){
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='-')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3)+(x<<1)+(s^48);
sc;
}
#undef sc
return x*f;
}
int FABS(int x){
if(x<0)
x=-x;
return x;
}
vector<int> g;
int main (){
int t=readint();
while(t--){
int a=readint(),b=readint();
int delta=FABS(a-b);
if(a==b)
g.push_back(0);
else if(delta==1)
g.push_back(0),g.push_back(1);
else{
if(delta&1)
g.push_back(delta/2),g.push_back(delta/2+1);
else
g.push_back(delta/2);
}
if(a<b)
swap(a,b);
for(int i=1;i<=b;i++){
if(delta&1)
g.push_back(delta/2+i*2),g.push_back(i*2+delta/2+1);
else
g.push_back(delta/2+i*2);
}
printf("%d\n",(int)g.size());
for(int i=0;i<g.size();i++){
printf("%d ",g[i]);
}
g.clear();
puts("");
}
return 0;
}
C Deep Down Below
TP
排序即可
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
typedef long long LL;
int readint(){
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='-')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3)+(x<<1)+(s^48);
sc;
}
#undef sc
return x*f;
}
int FABS(int x){
if(x<0)
x=-x;
return x;
}
int delta[maxn],ind[maxn];
int k[maxn];
bool cmp(int x,int y){
return delta[x]<delta[y];
}
int main (){
int t=readint();
while(t--){
memset(delta,0,sizeof(delta));
int n=readint();
for(int i=1;i<=n;i++){
ind[i]=i;
k[i]=readint();
int a=readint(),ans;
delta[i]=ans=a+1;
for(int j=2;j<=k[i];j++){
ans++;
a=readint();
if(a>=ans){
int t=ans;
ans=a+1;
delta[i]+=(ans-t);
}
}
}
sort(ind+1,ind+n+1,cmp);
int sum,need;
sum=need=delta[ind[1]];
for(int i=2;i<=n;i++){
sum+=k[ind[i-1]];
if(sum<delta[ind[i]]){
need+=(delta[ind[i]]-sum);
sum+=(delta[ind[i]]-sum);
}
}
printf("%d\n",need);
}
return 0;
}
D Up the Strip
设
d
p
[
i
]
dp[i]
dp[i]为改到i的方案数
通过减法转移的
d
p
[
i
+
1
]
→
d
p
[
n
]
dp[i+1]\to dp[n]
dp[i+1]→dp[n]
假设通过除
a
a
a得到的
d
p
[
i
∗
a
]
→
d
p
[
i
∗
a
+
a
−
1
]
dp[i*a]\to dp[i*a+a-1]
dp[i∗a]→dp[i∗a+a−1]
调和级数
+
+
+后缀和
#include <bits/stdc++.h>
using namespace std;
const int maxn = 4e6+5;
typedef long long LL;
int readint(){
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='-')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3)+(x<<1)+(s^48);
sc;
}
#undef sc
return x*f;
}
int FABS(int x){
if(x<0)
x=-x;
return x;
}
int dp[maxn],suf[maxn];
int main (){
int n=readint(),mo=readint();
dp[n]=suf[n]=1;
for(int i=n-1;i>=1;i--){
dp[i]=suf[i+1];
for(int j=2;j*i<=n;j++){
dp[i]=(0ll+dp[i]+suf[j*i]-suf[min(j*i+j,n+1)])%mo;
dp[i]=(dp[i]+mo)%mo;
}
suf[i]=(0ll+suf[i+1]+dp[i])%mo;
// cout<<dp[i]<<endl;
}
printf("%d\n",dp[1]);
return 0;
}
E. Bottom-Tier Reversals
TP
因为每次翻转的个数为奇数,那么翻转后位置的奇偶无法改变
如果奇数在偶数位上,或者偶数在奇数位上那就一辈子坐牢
考虑两两一组,将从小的大的翻转,只不过要倒序,才能保证过后的翻转不会有影响,具体看实现吧
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e3+50;
int readint(){
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='-')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3)+(x<<1)+(s^48);
sc;
}
#undef sc
return x*f;
}
int a[maxn],ans[maxn<<2],cnt;
int FABS(int x){
if(x<0)
x=-x;
return x;
}
int find(int n,int x){
for(int i=1;i<=n;i++)
if(a[i]==x)
return i;
}
void change(int x){
for(int i=1,j=x;i<=x/2;i++,j--)
swap(a[i],a[j]);
ans[++cnt]=x;
}
int main(){
int t=readint();
// cout<<t<<endl;
while(t--){
int n=readint();
cnt=0;
bool flag=0;
for(int i=1;i<=n;i++)
a[i]=readint();
for(int i=1;i<=n;i++)
if(FABS(a[i]-i)%2==1){
puts("-1");
flag=1;
break;
}
if(flag)
continue;
for(int i=1;i<=n/2;i++){
int pos1=find(n,i*2-1);
change(pos1);
int pos2=find(n,i*2);
change(pos2-1);
change(pos2+1);
change(3);
change(n-i*2+2);
}
change(n);
printf("%d\n",cnt);
for(int i=1;i<=cnt;i++){
printf("%d ",ans[i]);
}
puts("");
}
return 0;
}
1560
D Make a Power of Two
TP
搞清楚右边加数位的可以是任何数就可以了
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int readint(){
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='-')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3)+(x<<1)+(s^48);
sc;
}
#undef sc
return x*f;
}
char c[25];
struct zz{
char s[25];
int len;
};
vector<zz>g;
void prepare(){
LL num=1;
zz t;
t.s[1]='1';
t.len=1;
g.push_back(t);
for(int i=1;i<=60;i++){
num<<=1ll;
LL tot=num;
t.len=0;
while(tot){
t.s[++t.len]=48^(tot%10);
tot/=10;
}
reverse(t.s+1,t.s+t.len+1);
g.push_back(t);
}
}
int main (){
int t=readint();
prepare();
// for(int i=0;i<g.size();i++){
// for(int j=1;j<=g[i].len;j++){
// printf("%c",g[i].s[j]);
// }
// puts("");
// }
while(t--){
scanf("%s",c+1);
int n=strlen(c+1);
int ans=0x3f3f3f3f;
for(int i=0;i<g.size();i++){
int k=1;
for(int j=1;j<=n;j++)
if(k<=g[i].len&&c[j]==g[i].s[k])
++k;
k--;
ans=min(ans,n+g[i].len-k-k);
}
printf("%d\n",ans);
}
return 0;
}
E Polycarp and String Transformation
TP
口胡一个题
可以统计每个字母的最后出现的位置,然后排序得到他是第几个删除的,然后第
i
i
i轮删除的字母的出现个数一定是
i
i
i的倍数,逆向得到开头初串,然后模拟判可行性
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100005;
const int inf=1<<30;
const ll inff=1ll<<60;
inline int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return x*f;
}
string s,s1,s2,s3;
int l,f[35],ans[35],cnt;
void solve(){
cin>>s;
int sz=s.size();
for(int i=0;i<26;i++)f[i]=0;
cnt=0;
for(int i=sz-1;i>=0;i--){//倒序处理,得到第二问的解
if(!f[s[i]-'a'])ans[++cnt]=s[i]-'a';
f[s[i]-'a']++;
}
//注意我们求出来的第二问的解实际上也是倒序的,
//但是由于我比较懒就没有数组翻转了
l=0;
for(int i=1;i<=cnt;i++)
if(f[ans[i]]%(cnt-i+1)!=0){//cnt-i+1即ans[i]出现的轮数
puts("-1");//这个解已经不合法了
return;
}
else l+=f[ans[i]]/(cnt-i+1);
//求出初始串的长度l
s1="",s2="";
for(int i=0;i<l;i++)s1+=s[i];
int now=cnt;
//接下来是模拟,检验解的合法性
//s1为当前的字符串,s2为字符串对应的串t,s3用来更新s1
while(s1!=""){
s2+=s1;
s3="";
sz=s1.size();
for(int i=0;i<sz;i++)
if(ans[now]!=s1[i]-'a')
s3+=s1[i];
s1=s3;
now--;
}
//cout<<s2<<'\n';
if(s2==s){//如果检验得到的串t与给出的串t相同,那么这个解是合法的
for(int i=0;i<l;i++)cout<<s[i];
cout<<' ';
for(int i=cnt;i;i--)putchar(ans[i]+'a');
puts("");
}
else puts("-1");//解不合法,输出-1
}
int main(){int tests=1;tests=read();
while(tests--){
solve();
} return 0;
}
F2 Nearest Beautiful Number (hard version)
TP
这题挺水的,觉得不值
2100
2100
2100,显然,要让的到的数最小,必须让他前缀相同的越多,所以用
s
e
t
set
set不断塞数,如果大于可以用的数字,就向前找离他最近的那位不是
9
9
9的数位
+
+
++
++,然后将
n
n
n更新,因为已经知道那其间已经没有满足要求的数了,向上紧逼
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
int readint(){
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='-')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3)+(x<<1)+(s^48);
sc;
}
#undef sc
return x*f;
}
char s[15];
int main (){
int t=readint();
while(t--){
scanf("%s",s+1);
int k=readint();
int n=strlen(s+1);
while(1){
set<char> st;
st.clear();
for(int i=1;i<=n;i++)
st.insert(s[i]);
if(st.size()<=k){
for(int i=1;i<=n;i++)
printf("%c",s[i]);
puts("");
break;
}
st.clear();
int pos=1;
while(pos<=n){
st.insert(s[pos]);
if(st.size()>k){
while(s[pos]=='9')
pos--;
s[pos]++;
for(int i=pos+1;i<=n;i++)
s[i]='0';
break;
}
pos++;
}
}
}
return 0;
}
1559
A Mocha and Math
TP
数学题还是一个废物,考虑一个数位取和不能增加这一位为
1
1
1的数字的个数,所以每个数字的都为
1
1
1的数位才能保留,这有意思
#include <bits/stdc++.h>
using namespace std;
int readint(){
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='0')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3)+(x<<1)+(s^48);
sc;
}
#undef sc
return x*f;
}
int main (){
int t=readint();
while(t--){
int n=readint();
int ans=readint();
for(int i=2;i<=n;i++){
ans&=readint();
}
printf("%d\n",ans);
}
return 0;
}
B Mocha and Red and Blue
TP
写
d
p
dp
dp写麻了,我定义的是
d
p
[
i
]
[
0
/
1
]
[
0
/
1
]
dp[i][0/1][0/1]
dp[i][0/1][0/1]当前位置为
B
/
R
B/R
B/R的情况下最大串的最小值,以及当前连续位置串长度的最小值
哦,伞兵了,突然发现不是最长连续段,是指和前面相同的个数有多少个,自己想复杂了,
m
d
md
md
看看这俩兄弟的吧
模拟版
dp版
C Mocha and Hiking
TP
细想发现有三种情况可以完成整个路程
n
+
1
n+1
n+1号城市可通向
1
1
1 号城市。
n
n
n 号城市可通向
n
+
1
n + 1
n+1 号城市。
某座编号
k
k
k的城市能通向
n
+
1
n+1
n+1 号城市,且
n
+
1
n + 1
n+1 号城市能通向
k
+
1
k + 1
k+1 号城市。
#include<iostream>
#include<cstdio>
using namespace std;
int t,n;
int a[10005];
bool qwq;
int main(){
cin>>t;
for(int ii=1;ii<=t;ii++){
qwq=0;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
if(a[1]==1){
cout<<n+1<<" ";
for(int i=1;i<=n;i++)
cout<<i<<" ";
cout<<"\n";
continue;
}
if(a[n]==0){
for(int i=1;i<=n+1;i++)
cout<<i<<" ";
cout<<"\n";
continue;
}
for(int i=1;i<=n-1;i++)
if(a[i]==0&&a[i+1]==1){
for(int j=1;j<=i;j++)
cout<<j<<" ";
cout<<n+1<<" ";
for(int j=i+1;j<=n;j++)
cout<<j<<" ";
cout<<"\n";
qwq=1;
break;
}
if(qwq==0)
cout<<"-1\n";
}
return 0;
}
D Mocha and Diana (Hard Version)
TP
真牛逼,说实话不看题解把我卖了都想不到
先证明小的那棵树连通块的个数一定为
1
1
1
设小的为
A
A
A,大的为
B
B
B
假设
A
A
A如今已经不能操作了,任意条边相连都会影响
B
B
B图为树
设
A
A
A中其中有两个树
x
,
y
x,y
x,y
由于不能再连边了,所以两颗树的任何节点在另一个森林中都可以联通
所以其中有一个连通块一定为
1
1
1个
由此贪心分析可知,随意加可行边即可。
考虑优化贪心。
考虑一个中心点
s
s
s。
我们先让所有点与
s
s
s 尝试连边。
然后连完后令
A
A
A 图中与
s
s
s 不连通的点集为
L
L
L,
B
B
B 图中与
s
s
s 不连通的点集为
R
R
R。
显然
L
∩
R
=
∅
L\cap R=\varnothing
L∩R=∅。
考虑
l
∈
L
l\in L
l∈L 和
r
∈
R
r\in R
r∈R。
由定义有 A 图中
l
l
l 与
s
s
s 不连通,
r
r
r 与
s
s
s 连通,
B
B
B 图相反。
那么任意
l
l
l与
r
r
r 都可连边。
然后只要随意配对完
L
L
L 或
R
R
R 就行了,此时一幅图变成一个连通块。
时间复杂度
O
(
n
α
(
n
)
)
O(n\alpha(n))
O(nα(n))。
E Mocha and Stars
TP
如果不考虑
g
c
d
gcd
gcd的话,就是一个裸的背包,考虑
g
c
d
gcd
gcd后可以考虑一个容斥
在处理
gcd
\gcd
gcd 为
x
x
x 的问题时,可以倒着枚举一个
i
i
i,设
a
n
s
i
ans_i
ansi是
gcd
\gcd
gcd 恰好为
i
i
i 的方案数,
f
i
f_i
fi为
gcd
\gcd
gcd 是
i
i
i 的倍数的方案数。
那么就可以用
a
n
s
g
=
f
g
−
∑
2
m
/
g
a
n
s
i
∗
g
ans_g = f_g - \sum_{2}^{m/g} ans_{i*g}
ansg=fg−∑2m/gansi∗g 来求解。
很好的博客
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
const int mo = 998244353;
int readint(){
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='-')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3)+(x<<1)+(s^48);
sc;
}
#undef sc
return x*f;
}
int dp[maxn],pre[maxn],upd[maxn];//dp,前缀和,修改后的一些值
int f[maxn],ans[maxn];
int l[55],r[55];
int main (){
int n=readint(),m=readint();
for(int i=1;i<=n;i++)
l[i]=readint(),r[i]=readint();
for(int d=m;d>=1;d--){
for(int i=0;i<=m/d;i++)
dp[i]=0,pre[i]=0,upd[i]=0;
pre[0]=upd[0]=1;
for(int j=1;j<=m/d;j++)
pre[j]=(0ll+pre[j-1]+upd[j])%mo;
for(int i=1;i<=n;i++){
for(int j=m/d;j>=ceil(l[i]*1.0/d);j--){
if(j>r[i]/d)
dp[j]=(0ll+pre[j-(int)ceil(l[i]*1.0/d)]-pre[j-r[i]/d-1]+mo)%mo;
else
dp[j]=pre[j-(int)ceil(l[i]*1.0/d)];
}
for(int j=0;j<=m/d;j++)
upd[j]=dp[j],dp[j]=0;
pre[0]=upd[0];
for(int j=1;j<=m/d;j++)
pre[j]=(0ll+pre[j-1]+upd[j])%mo;
}
for(int i=0;i<=m/d;i++)
f[d]=(0ll+upd[i]+f[d])%mo;
ans[d]=f[d];
for(int i=d+d;i<=m;i+=d)
ans[d]=(0ll+ans[d]-ans[i]+mo)%mo;
}
printf("%d\n",ans[1]);
return 0;
}
1557
A Ezzat and Two Subsequences
#include<bits/stdc++.h>
using namespace std;
int main(){
int t; cin>>t;
while(t--){
double n; cin>>n;
int maxx=-0x3f3f3f3f;
double sum=0;
int k=n;
while(k--){
int x;
cin>>x;
sum+=x;
maxx=max(x,maxx);//更新最大值
}
printf("%0.9lf\n",(sum-maxx)/(n-1)+maxx);
}
return 0;//好习惯
}
B Moamen and k-subarrays
刚开始没看到数字都不相同
TP
#include<bits/stdc++.h>
using namespace std;
int t;
int n,k;
struct node {
int name,x;
}a[100005];
bool cmp(node a,node b) {
return a.x<b.x;
}
int main() {
cin>>t;
while(t--) {
cin>>n>>k;
for(int i=1;i<=n;i++)cin>>a[i].x,a[i].name=i;
sort(a+1,a+n+1,cmp);
int cnt=1;
for(int i=2;i<=n;i++)
if(a[i].name!=a[i-1].name+1)cnt++;
if(cnt>k)cout<<"No"<<endl;
else cout<<"Yes"<<endl;
}
}
C Moamen and XOR
TP
考虑到
n
n
n为奇数时只能等于即可
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define int LL
const int maxn = 2e5+5;
const int mo = 1e9+7;
int readint(){
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='-')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3)+(x<<1)+(s^48);
sc;
}
#undef sc
return x*f;
}
int fac[maxn],inv[maxn];
void init(int n){
fac[0]=inv[0]=inv[1]=1;
for(int i=1;i<=n;i++)
fac[i]=1ll*fac[i-1]*i%mo;
for(int i=2;i<=n;i++)
inv[i]=1ll*(mo-mo/i)*inv[mo%i]%mo;
for(int i=2;i<=n;i++)
inv[i]=1ll*inv[i-1]*inv[i]%mo;
}
int C(int n,int m){
if(n<=0||m<=0||n<m)
return 1;
return 1ll*fac[n]*inv[n-m]%mo*inv[m]%mo;
}
int qkpow(int a,int b){
int ans=1;
while(b){
if(b&1)
ans=1ll*ans*a%mo;
b>>=1;
a=1ll*a*a%mo;
}
return ans%mo;
}
signed main (){
init(maxn-5);
int t=readint();
while(t--){
int n=readint(),k=readint();
int sum=0,ans=0;
for(int i=0;i<n;i+=2){//ijһλÏàµÈµÄ·½°¸Êý
sum=(0ll+sum+C(n,i))%mo;
}
// cout<<sum<<endl;
if(n&1)
ans=(0ll+ans+qkpow((sum+1)%mo,k))%mo;
else{
for(int i=0;i<k;i++)
ans=(0ll+ans+qkpow(sum,i)%mo*qkpow(qkpow(2,n),k-i-1)%mo)%mo;
ans=(0ll+ans+qkpow(sum,k))%mo;
}
printf("%d\n",ans);
}
return 0;
}
1549
A Gregor and Cryptography
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2e5+5;
const int mo = 1e9+7;
int readint(){
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='-')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3)+(x<<1)+(s^48);
sc;
}
#undef sc
return x*f;
}
signed main (){
int t=readint();
while(t--){
int p=readint();
printf("%d %d\n",p/2,p/2*2);
}
return 0;
}
B Gregor and the Pawn Game
#include<bits/stdc++.h>
using namespace std;
int t,n;
string s1,s2;
int ans1[200005],ans2[200005];
bool vis[200005];
int main(){
cin>>t;
while(t--){
memset(vis,0,sizeof(vis));//多测不清空,保龄两行泪
cin>>n;
cin>>s1>>s2;
int num=0;
for(int i=1;i<=n;i++){
ans1[i]=s1[i-1]-'0';
ans2[i]=s2[i-1]-'0';
}
for(int i=1;i<=n;i++){
if(ans1[i]==0&&!vis[i]&&ans2[i]==1){
num++;
vis[i]=1;
}
else if(i!=1&&ans1[i-1]==1&&ans1[i]==1&&!vis[i-1]&&ans2[i]==1){
num++;
vis[i-1]=1;//被友军走了
}
/*
当然,第一个和最后一个因为地理原因要特殊考虑
*/
else if(i!=n&&ans1[i+1]==1&&ans1[i]==1&&!vis[i+1]&&ans2[i]==1){
num++;
vis[i+1]=1;
}
}
cout<<num<<'\n';
}
return 0;
}
Web of Lies
TP
易得与周围邻居相比最小便可以留下来
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
long long n,m,t,out[200050],ans;
int main(){
long long i,j,u,v;
cin>>n>>m;
ans=n;//一开始都能活
while(m--){
cin>>u>>v;
if(u>v) swap(u,v);
if(out[u]==0) ans--;
out[u]++;
}
cin>>t;
while(t--){
cin>>j;
if(j==1){
cin>>u>>v;
if(u>v) swap(u,v);
if(out[u]==0) ans--;
out[u]++;
}
if(j==2){
cin>>u>>v;
if(u>v) swap(u,v);
out[u]--;
if(out[u]==0) ans++;
}
if(j==3){
cout<<ans<<endl;
}
}
return 0;
}
D Integers Have Friends
TP
设一连串的数除数为
m
m
m,余数为
b
b
b
有
a
i
=
k
i
∗
m
+
b
a_i=k_i*m+b
ai=ki∗m+b
a
i
+
1
−
a
i
=
m
∗
(
k
i
+
1
−
k
i
)
a_{i+1}-a_i=m*(k_{i+1}-k_i)
ai+1−ai=m∗(ki+1−ki)
发现差的绝对值求
g
c
d
gcd
gcd便是所除的
m
m
m
得到新的数组
b
i
=
a
i
+
1
−
a
i
b_i=a_{i+1}-a_i
bi=ai+1−ai,求最长区间的
g
c
d
gcd
gcd大于一即可
建议写
s
t
st
st表,不然容易
T
T
T,毕竟
O
(
n
∗
l
o
g
2
n
)
O(n*log^2n)
O(n∗log2n)
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 2e5+5;
LL readLL(){
LL x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='-')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3ll)+(x<<1ll)+(s^48);
sc;
}
#undef sc
return x*f;
}
int readint(){
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='-')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3)+(x<<1)+(s^48);
sc;
}
#undef sc
return x*f;
}
LL FABS(LL x){
if(x<0)
x=-x;
return x;
}
LL gcd(LL a,LL b){
if(!b)
return a;
return gcd(b,a%b);
}
LL st[maxn][20],a[maxn],b[maxn];
void prepare(int n){
for(int i=1;i<n;i++)
st[i][0]=FABS(a[i+1]-a[i]);
for(int j=1;j<=19;j++){
for(int i=1;i+(1<<(j-1))-1<n;i++)
st[i][j]=gcd(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}
LL query(int x,int l,int r){
int k=log2(r-l+1);
return gcd(st[l][k],st[r-(1<<k)+1][k]);
}
bool check(int n,int len){
for(int i=1;i<=n-len+1;i++){
if(query(1,i,i+len-2)>1)
return 1;
}
return 0;
}
signed main (){
int t=readint();
while(t--){
// memset(tree,0,sizeof(tree));
// memset(a,0,sizeof(a));
int n=readint();
for(int i=1;i<=n;i++)
a[i]=readLL();
if(n==1){
printf("1\n");
continue;
}
prepare(n);
int l=2,r=n,ans=1;
while(l<=r){
int mid=(l+r)>>1;
if(check(n,mid)){
ans=mid;
l=mid+1;
}
else
r=mid-1;
}
printf("%d\n",ans);
}
return 0;
}
CF1555
D Say No to Palindromes
TP
可以尝试模拟一下:
a
a
a 不回文,尝试添加一个字符,可以添加
b
b
b 或者
c
c
c, 这里选择添加
b
b
b, 于是字符串变成了
a
b
ab
ab; 再次尝试添加字符,发现只能添加
c
c
c,于是字符串变成了
a
b
c
abc
abc; 再次尝试添加字符,发现只能添加
a
a
a,于是字符串变成了
a
b
c
a
abca
abca;再次尝试添加字符,发现只能添加 b,于是字符串变成了 abcab;再次尝试添加字符,发现只能添加 c,于是字符串变成了
a
b
c
a
b
c
abcabc
abcabc。
发现不回文的字符串只能使一个以前三个字符组成的循环,而且前三种字符只有
A
3
3
A_3^3
A33
种排列方式。
对于每次询问,可以枚举这
6
6
6 种排列方式,取最小值即可。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,m;
char a[N],s[7][3]={{'a','b','c'},{'a','c','b'},{'b','a','c'},{'b','c','a'},{'c','a','b'},{'c','b','a'}};
int sum[N][7];
int main(){
scanf("%d%d",&n,&m);
scanf("%s",a+1);
for(int k=0;k<=6;k++){
int now=0;
for(int i=1;i<=n;i++,now++){
if(now==3) now=0;
if(a[i]!=s[k][now]) sum[i][k]++;
sum[i][k]+=sum[i-1][k];
}//六种情况的前缀和
}
for(int i=1,l,r;i<=m;i++){
scanf("%d%d",&l,&r);
int ans=1e9;
for(int k=0;k<=6;k++){
ans=min(ans,sum[r][k]-sum[l-1][k]);
}//选取代价最小的那一个
printf("%d\n",ans);
}
return 0;
}
E Boring Segments
TP
双指针维护上下界即可
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
int readint(){
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='-')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3)+(x<<1)+(s^48);
sc;
}
#undef sc
return x*f;
}
int tree[maxn<<2],lazy[maxn<<2];
struct zz{
int l,r,w;
}s[maxn];
bool cmp(const zz &x,const zz &y){
return x.w<y.w;
}
void pushdown(int x){
if(lazy[x]!=0){
lazy[x<<1]+=lazy[x];
tree[x<<1]+=lazy[x];
lazy[x<<1|1]+=lazy[x];
tree[x<<1|1]+=lazy[x];
lazy[x]=0;
}
}
void update(int x,int l,int r,int goal_l,int goal_r,int val){
if(goal_l<=l&&goal_r>=r){
lazy[x]+=val;
tree[x]+=val;
return;
}
pushdown(x);
int mid=(l+r)>>1;
if(goal_l<=mid)
update(x<<1,l,mid,goal_l,goal_r,val);
if(goal_r>mid)
update(x<<1|1,mid+1,r,goal_l,goal_r,val);
tree[x]=min(tree[x<<1],tree[x<<1|1]);
}
int main (){
int n=readint(),m=readint()-1;
for(int i=1;i<=n;i++){
s[i].l=readint(),s[i].r=readint()-1,s[i].w=readint();
}
sort(s+1,s+n+1,cmp);
int L=0,R=0,ans=0x3f3f3f3f;
while(1){
while(R+1<=n&&tree[1]==0)
R++,update(1,1,m,s[R].l,s[R].r,1);
if(tree[1]==0)
break;
while(L+1<=R&&tree[1]>0)
L++,update(1,1,m,s[L].l,s[L].r,-1);
// cout<<"ans:"<<L<<" "<<R<<endl;
ans=min(ans,s[R].w-s[L].w);
}
cout<<ans;
return 0;
}
F Good Graph
TP
因为图中每个环的权值异或和为
1
1
1,所以由两个环拼成的环的异或和就是
0
0
0,不合法。因此,合法的图中每条边一定至多属于一个环。
怎么维护这个东西呢?我们只维护一颗树就好了,加入的一条边如果和树上的一条路径形成环,我们就把这条路经上的边都打上标记,在这之前要进行判断,如果边两端之间的路径上已经存在标记或形成的环的异或和不为
1
1
1,则不合法。如果新加入的边两端点本来不连通,直接加入即可。
这是一个动态维护的,显然可以用
L
C
T
LCT
LCT
像我这种比较菜的就离线下来写树剖
#include <bits/stdc++.h>
using namespace std;
const int maxm = 5e5+5;
const int maxn = 3e5+5;
int readint(){
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='-')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3)+(x<<1)+(s^48);
sc;
}
#undef sc
return x*f;
}
struct dsu{
int f[maxn];
void makeset(int n){
for(int i=1;i<=n;i++)
f[i]=i;
}
int findset(int x){
if(x!=f[x])
f[x]=findset(f[x]);
return f[x];
}
int merge(int x,int y){
int fx=findset(x),fy=findset(y);
if(fx==fy)
return 0;
f[fx]=fy;
return 1;
}
}a;
struct zz{
int x,y,w;
int type;
}s[maxm];
struct edge{
int v,w,nxt;
edge(){};
edge(int V,int W,int N){
v=V;
w=W;
nxt=N;
}
}e[maxn<<1];
int head[maxn],edge_cnt=-1;
void addedge(int u,int v,int w){
e[++edge_cnt]=edge(v,w,head[u]);
head[u]=edge_cnt;
e[++edge_cnt]=edge(u,w,head[v]);
head[v]=edge_cnt;
}
int dep[maxn],xor_[maxn],son[maxn],siz[maxn];
int fa[maxn];
void dfs1(int u,int pre){
siz[u]=1;dep[u]=dep[pre]+1;
fa[u]=pre;
for(int i=head[u];~i;i=e[i].nxt){
int v=e[i].v;
if(v==pre)
continue;
xor_[v]=xor_[u]^e[i].w;
dfs1(v,u);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]])
son[u]=v;
}
}
int dfn[maxn],rdfn[maxn],dfn_cnt,top[maxn];
void dfs2(int u,int anc){
top[u]=anc;
dfn[u]=++dfn_cnt;
rdfn[dfn_cnt]=u;
if(son[u])
dfs2(son[u],anc);
for(int i=head[u];~i;i=e[i].nxt){
int v=e[i].v;
if(v==fa[u]||v==son[u])
continue;
dfs2(v,v);
}
}
int tree[maxn<<2],lazy[maxn<<2];
void pushdown(int x){
if(lazy[x]){
lazy[x<<1]=lazy[x];
lazy[x<<1|1]=lazy[x];
tree[x<<1]=lazy[x];
tree[x<<1|1]=lazy[x];
lazy[x]=0;
}
}
void update(int x,int l,int r,int goal_l,int goal_r){
if(goal_l<=l&&goal_r>=r){
tree[x]=1;
lazy[x]=1;
return;
}
pushdown(x);
int mid=(l+r)>>1;
if(goal_l<=mid)
update(x<<1,l,mid,goal_l,goal_r);
if(goal_r>mid)
update(x<<1|1,mid+1,r,goal_l,goal_r);
tree[x]=max(tree[x<<1],tree[x<<1|1]);
}
int query(int x,int l,int r,int goal_l,int goal_r){
if(l>=goal_l&&goal_r>=r)
return tree[x];
pushdown(x);
int mid=(l+r)>>1,ans=0;
if(goal_l<=mid)
ans=max(ans,query(x<<1,l,mid,goal_l,goal_r));
if(goal_r>mid)
ans=max(ans,query(x<<1|1,mid+1,r,goal_l,goal_r));
tree[x]=max(tree[x<<1],tree[x<<1|1]);
return ans;
}
bool findcircle(int x,int y){
int tx=top[x],ty=top[y];
while(tx!=ty){
if(dep[tx]<dep[ty])
swap(x,y),swap(tx,ty);
if(query(1,1,dfn_cnt,dfn[tx],dfn[x])>0)
return 1;
x=fa[tx];
tx=top[x];
}
if(dep[x]>dep[y])
swap(x,y);
if(query(1,1,dfn_cnt,dfn[x]+1,dfn[y])>0)
return 1;
return 0;
}
void modify(int x,int y){
int tx=top[x],ty=top[y];
while(tx!=ty){
if(dep[tx]<dep[ty])
swap(tx,ty),swap(x,y);
update(1,1,dfn_cnt,dfn[tx],dfn[x]);
x=fa[tx];
tx=top[x];
}
if(x==y)
return;
if(dep[x]>dep[y])
swap(x,y);
update(1,1,dfn_cnt,dfn[x]+1,dfn[y]);
}
int main (){
memset(head,-1,sizeof(head));
int n=readint(),m=readint();
for(int i=1;i<=m;i++)
s[i].x=readint(),s[i].y=readint(),s[i].w=readint();
a.makeset(n);
for(int i=1;i<=m;i++){
int u=s[i].x,v=s[i].y;
if(a.merge(u,v))
addedge(u,v,s[i].w),s[i].type=1;
}
for(int i=1;i<=n;i++){
if(!dfn[i])
dfs1(i,0),dfs2(i,i);
}
for(int i=1;i<=m;i++){
if(!s[i].type){
int u=s[i].x,v=s[i].y;
bool flag=0;
if((xor_[u]^xor_[v]^s[i].w)==0)
continue;
if(findcircle(u,v))
flag=1;
if(flag)
continue;
modify(u,v);
s[i].type=1;
}
}
int sum=0;
for(int i=1;i<=m;i++)
if(s[i].type)
puts("YES");
else
puts("NO");
return 0;
}
CF1554
A Cherry
TP
首先,我们可以考虑设
a
i
a_i
ai为我们选的最优序列里的最大值。
接下来要确定最小值。
每次加进一个数,只能使最小值不变或者令它更小。
所以,我们考虑只加入它的相邻的一个。
也就是说,最优序列肯定是由两个相邻的数组成的。
C Mikasa
TP
由于不可能一个一个枚举,所以这道题要用到异或的一些性质。设
i
∈
{
0
,
1
,
…
,
m
}
i\in \{0,1,\ldots ,m \}
i∈{0,1,…,m},与其让
n
n
n 去一个一个异或
i
i
i,不如构造一个答案,让这个答案不属于这个集合。
a
⊕
b
=
c
a\oplus b=c
a⊕b=c
⇒
a
⊕
c
=
b
\Rightarrow a\oplus c=b
⇒a⊕c=b
根据这个性质,为了使
n
⊕
i
≠
k
n\oplus i \ne k
n⊕i=k
其实只要构造出一个
k
k
k,使得
n
⊕
k
∉
{
0
,
1
,
…
,
m
}
n\oplus k \notin \{0,1,\ldots ,m \}
n⊕k∈/{0,1,…,m}
也就是
n
⊕
k
≥
m
+
1
n\oplus k \ge m+1
n⊕k≥m+1,接下来就是按位进行构造了。
从高位到低位一位一位构造,对于第 ii 位,我们可以分四种情况讨论:
n
n
n 第
i
i
i 位为
1
1
1,
(
m
+
1
)
(m+1)
(m+1) 第
i
i
i 位也为
1
1
1 时,
k
k
k 第
i
i
i位赋为
0
0
0 就可以了,因为
1
⊕
0
=
1
1\oplus 0=1
1⊕0=1,
≥
m
\ge m
≥m 的同位。
n
n
n 第
i
i
i 位为
1
1
1,
(
m
+
1
)
(m+1)
(m+1) 第
i
i
i 位为 0 时,k 第
i
i
i 位赋为
0
0
0,因为
1
⊕
0
=
1
,
>
m
1\oplus 0=1,>m
1⊕0=1,>m 的同位;但是
k
k
k 的之后所有位也都得是
0
0
0 了。为什么?因为我们构造的 k 要尽可能小。
n
n
n 第
i
i
i 位为
0
0
0,
(
m
+
1
)
(m+1)
(m+1) 第
i
i
i 位为
1
1
1 时,
k
k
k 第
i
i
i 位赋为
1
1
1,因为
0
⊕
1
=
1
,
≥
0\oplus 1=1,\ge
0⊕1=1,≥ m 的同位。
n
n
n第
i
i
i 位为
0
0
0,
(
m
+
1
)
(m+1)
(m+1) 第
i
i
i 位也为
0
0
0 时,
k
k
k 第
i
i
i 位赋为
0
0
0,因为
0
⊕
0
=
0
,
≥
m
0\oplus 0=0,\ge m
0⊕0=0,≥m 的同位。
D Diane
TP
我们考虑一个全是同一字母的串。如果长度为奇数,那么它的各个奇数长度的子串出现次数为奇数个,偶数长度为偶数个。如果长度为偶数则刚好相反,奇数长度子串出现偶数次,偶数长度出现奇数次。
发现这个的话,之后就很简单了,就是把奇数串,偶数串都放入传中,中间加一个串防止两边影响。很容易证明这样就是对的。
CF1546D AquaMoon and Chess
仔细研究发现 11 11 11一组是可以随便移动,不受影响,所以我们可以两个 1 1 1分为一组,如果连续 1 1 1的个数为偶数依旧,依旧两两一组,如果发现为奇数个 1 1 1那把最后的 10 10 10分为一组,令 A = 11 , B = 10 A=11,B=10 A=11,B=10发现 A B AB AB是可以随意交换的, A A A和 0 0 0也是可以随便交换的, B B B和 B B B就不可以(好像是一句废话,换了当没换), B B B和 0 0 0也不可以换,所以 B B B和 0 0 0本质是一样的,相对位置不改变,所以只用统计 11 11 11的个数为 a a a和 0 0 0的个数为 b b b,答案为 ( a + b b ) {a+b}\choose{b} (ba+b)
CF1542C Strange Function
考虑反向定义
令
F
(
k
)
F(k)
F(k)为最小的不为因子为
k
k
k的数的个数
其中
2
<
=
k
<
=
45
2<=k<=45
2<=k<=45,因为
l
c
m
(
1
,
2
,
.
.
.
k
)
>
1
0
16
lcm(1,2,...k)>10^{16}
lcm(1,2,...k)>1016
显然
F
(
k
)
=
n
/
l
c
m
(
1
,
2...
,
k
−
1
)
−
n
/
l
c
m
(
1
,
2....
k
)
F(k)=n/lcm(1,2...,k-1)-n/lcm(1,2....k)
F(k)=n/lcm(1,2...,k−1)−n/lcm(1,2....k)
CF1542D Priority Queue
考虑的每个数单独求贡献,于是可以在
+
x
+\ \ \ \ x
+ x对这个数单独进行
d
p
dp
dp求他一共有多少种方案
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示前
i
i
i个操作已经处理,总共有
j
j
j个数比
x
x
x小,因为比他小的数比他先删除
p
o
s
pos
pos为我们研究的
+
x
+\ \ \ \ x
+ x为第几次操作
考虑
i
<
p
o
s
i<pos
i<pos的情况
若当前这一位是
−
-
−号,那么就有
d
p
i
,
j
=
d
p
i
−
1
,
j
+
d
p
i
−
1
,
j
+
1
dp_{i,j}=dp_{i-1,j}+dp_{i-1,j+1}
dpi,j=dpi−1,j+dpi−1,j+1,另外特别注意一下
d
p
i
,
0
dp_{i,0}
dpi,0需要再次
+
d
p
i
−
1
,
0
+ dp_{i-1,0}
+dpi−1,0
不选这个
−
-
−号,不变,选了这个
−
-
−号并且
j
j
j 本来就已经是
0
0
0 了,也是不变,因此
d
p
i
−
1
,
0
dp_{i-1,0}
dpi−1,0要算两遍)
如果是
+
+
+号,并且当前的
v
a
l
j
>
v
a
l
p
o
s
val_{j}>val_{pos}
valj>valpos
那么选不选都无所谓,就有
d
p
i
,
j
=
d
p
i
−
1
,
j
∗
2
dp_{i,j}=dp_{i-1,j}*2
dpi,j=dpi−1,j∗2
否则的话,如果选,
j
j
j就增加
1
1
1,否则不变,就有
d
p
i
,
j
=
d
p
i
−
1
,
j
+
d
p
i
−
1
,
j
−
1
dp_{i,j}=dp_{i-1,j}+dp_{i-1,j-1}
dpi,j=dpi−1,j+dpi−1,j−1
考虑
i
>
p
o
s
i>pos
i>pos的情况
若当前这一位是
−
-
−号,
d
p
i
,
0
dp_{i,0}
dpi,0不需要再次
+
d
p
i
−
1
,
0
+ dp_{i-1,0}
+dpi−1,0,因为这样就会把
v
a
l
p
o
s
val_{pos}
valpos给删掉
其次为
+
+
+且可选可不选的的一定要满足
v
a
l
p
o
s
≤
v
a
l
j
val_{pos}\le val_{j}
valpos≤valj,不然方案重复等死了
#include <bits/stdc++.h>
using namespace std;
const int maxn = 505;
const int mo = 998244353;
int readint(){
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){
if(s=='-')
f=-1;
sc;
}
while(s>='0'&&s<='9'){
x=(x<<3)+(x<<1)+(s^48);
sc;
}
#undef sc
return x*f;
}
int a[maxn],dp[maxn][maxn];
int ans;
void getdp(int x,int n){
for(int i=0;i<=n+1;i++)
for(int j=0;j<=n+1;j++)
dp[i][j]=0;
dp[0][0]=1;
for(int i=1;i<x;i++)
if(a[i]==0){
for(int j=0;j<=n;j++)
dp[i][j]=(0ll+dp[i-1][j]+dp[i-1][j+1])%mo;
dp[i][0]=(0ll+dp[i][0]+dp[i-1][0])%mo;
}
else{
for(int j=0;j<=n;j++){
if(a[i]>a[x])
dp[i][j]=1ll*dp[i-1][j]*2%mo;
else
dp[i][j]=(0ll+dp[i-1][j]+dp[i-1][j-1])%mo;
}
}
for(int j=0;j<=n;j++)
dp[x][j]=dp[x-1][j];
for(int i=x+1;i<=n;i++){
if(a[i]==0){
for(int j=0;j<=n;j++)
dp[i][j]=(0ll+dp[i-1][j]+dp[i-1][j+1])%mo;
}
else{
for(int j=0;j<=n;j++){
if(a[i]>=a[x])
dp[i][j]=1ll*dp[i-1][j]*2%mo;
else
dp[i][j]=(0ll+dp[i-1][j]+dp[i-1][j-1])%mo;
}
}
}
int sum=0;
for(int j=0;j<=n;j++)
sum=(0ll+sum+dp[n][j])%mo;
ans=(0ll+ans+1ll*sum*a[x]%mo)%mo;
}
int main (){
int n=readint();
for(int i=1;i<=n;i++){
char op[3];
scanf("%s",op);
if(op[0]=='-')
continue;
a[i]=readint();
}
for(int i=1;i<=n;i++){
if(a[i]!=0)
getdp(i,n);
}
cout<<ans<<endl;
return 0;
}