目录
F.Sequence(线段树 单点更新+区间查询)
题意:
定义,和
给定到,有两种操作:
操作1:格式为0 x y,把ax改为y
操作2:格式为1 x y,求F(x,y)
分析:
不难发现,F(l,r)可由以下项异或得到:
1*(r-l+1)个
2*(r-l)个
3*(r-l-1)个
······
(r-l+1)*1个
显然,、、······等项异或结果为0。
分情况讨论:
情况1:r-l+1为奇数,则
情况2:r-l+1为偶数,则答案为0
思路:
建2棵线段树,第一棵tr1只含有a数组的奇数项,偶数项全为0,另一颗tr2只含有a数组的偶数项,奇数项全为0。
那么r-l+1为奇数时:若l为奇数,用tr1线段树求F(l,r);若l为偶数,用tr2线段树求F(l,r)。
r-l+1为偶数时:答案为0
单点更新时只需把更新后的值和原值异或得到的值传入update函数进行更新即可。
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
using namespace std;
const int MAXN=1e5+5;
typedef long long ll;
int a[MAXN],t1[MAXN],t2[MAXN],ans;
struct node{
int l,r,sum;
}tr1[MAXN<<2],tr2[MAXN<<2];
void build(node *tr,int *arr,int rt,int l,int r){
tr[rt].l=l;
tr[rt].r=r;
if(l==r){
tr[rt].sum=arr[l];
return;
}else{
int mid=(l+r)>>1;
build(tr,arr,rt<<1,l,mid);
build(tr,arr,rt<<1|1,mid+1,r);
tr[rt].sum=tr[rt<<1].sum xor tr[rt<<1|1].sum;
}
}
void update(node *tr,int rt,int ql,int qr,int val){
if(ql==tr[rt].l&&tr[rt].r==qr){
tr[rt].sum=tr[rt].sum xor val;
return;
}
int mid=(tr[rt].l+tr[rt].r)>>1;
if(qr<=mid)
update(tr,rt<<1,ql,qr,val);
else if(mid<ql)
update(tr,rt<<1|1,ql,qr,val);
else{
update(tr,rt<<1,ql,mid,val);
update(tr,rt<<1|1,mid+1,qr,val);
}
}
void query(node *tr,int rt,int ql,int qr){
if(ql<=tr[rt].l&&tr[rt].r<=qr){
ans=ans xor tr[rt].sum;
return;
}
if(tr[rt].l==tr[rt].r)
return;
int mid=(tr[rt].l+tr[rt].r)>>1;
if(qr<=mid)
query(tr,rt<<1,ql,qr);
else if(mid<ql)
query(tr,rt<<1|1,ql,qr);
else{
query(tr,rt<<1,ql,mid);
query(tr,rt<<1|1,mid+1,qr);
}
}
int main(){
int T,n,m,t,x,y;
scanf("%d",&T);
for(int i=1;i<=T;i++){
scanf("%d%d",&n,&m);
for(int j=1;j<=n;j++){
scanf("%d",&a[j]);
if(j&1){
t1[j]=a[j];
t2[j]=0;
}else{
t1[j]=0;
t2[j]=a[j];
}
}
build(tr1,t1,1,1,n);
build(tr2,t2,1,1,n);
printf("Case #%d:\n",i);
for(int j=1;j<=m;j++){
scanf("%d%d%d",&t,&x,&y);
if(t==0){
if(x&1){
update(tr1,1,1,n,y xor a[x]);
}else{
update(tr2,1,1,n,y xor a[x]);
}
a[x]=y;//用完原值后更新a数组
}else{
ans=0;
if((y-x+1)&1){//若r-l+1为奇数
if(x&1){//若l为奇数,用线段树tr1,否则用tr2
query(tr1,1,x,y);
}else{
query(tr2,1,x,y);
}
}
printf("%d\n",ans);
}
}
}
return 0;
}
G.Winner(思维)
题意:
n个人,每个人在不同模式下有不同的能力值,共有三种模式。任选模式任选未被淘汰的人进行n-1场比赛(该模式下能力值高的人获胜),最后一个人获胜。问:给定第k个人,他有没有可能赢?
思路:
假定第i个人有可能赢,那么你只要在某一模式下能赢他,那么你就能赢。因为你能赢第i个人,然后他帮你赢其他所有人。
首先输入的同时记录该模式下能力值最大的那个人的id,这个人有可能赢。然后记录能赢的人他们的不同模式下能力值的最小值。(即你只需要在某一模式下大于这个最小能力值,你也能赢)
每次从头遍历,如果找到一个还不知道能不能赢的人,他在某一模式下比能赢的所有人在该模式下能力值的最小值还大,那么这个人也能赢,如果找不到,结束循环,剩下所有还不知道能不能赢的人都是不可能赢的。
队友敲的代码:
#include<cstdio>
#include<cstring>
int a[4][100010];
bool vis[100010];
int vnum[4]={0x3f3f3f3f,0x3f3f3f3f,0x3f3f3f3f,0x3f3f3f3f};
void changemin(int j){
for(int i=1;i<=3;i++){
if(a[i][j]<vnum[i]) vnum[i]=a[i][j];
// printf("%d\n",vnum[i]);
}
}
int main(){
int n,m;
int id[4];
memset(vis,false,sizeof(vis));
scanf("%d%d",&n,&m);
for(int i=1;i<=3;i++){
int maxn=0;
for(int j=1;j<=n;j++){
scanf("%d",&a[i][j]);
if(a[i][j]>maxn) {
id[i]=j;
maxn=a[i][j];
}
}
vis[id[i]]=true;
}
//for(int i=1;i<=3;i++)printf("%d ",id[i]);
//printf("\n");
for(int i=1;i<=3;i++){
for(int j=1;j<=3;j++){
//printf("%d %d %d ",i,id[j],a[i][id[j]]);
if(vnum[i]>a[i][id[j]]) vnum[i]=a[i][id[j]];
}
//printf("\n");
}
int modi=1;
//for(int i=1;i<=3;i++)printf("%d\n",vnum[i]);
while(modi){
int flag=0;
for(int i=1;i<=3;i++){
for(int j=1;j<=n;j++){
if(vis[j]) continue;
if(a[i][j]>vnum[i]){
changemin(j);
flag=1;
vis[j]=true;
}
}
}
if(flag==0) modi=0;
}
int p;
for(int i=1;i<=m;i++) {
scanf("%d",&p);
if(vis[p]) printf("YES\n");
else printf("NO\n");
}
}
J.Prefix(字典树)
分析:
队友说是字典树模板题,emmm,然而弱鸡的我并不会字典树······
队友敲的代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
using namespace std;
const int maxn=1e5+5;
typedef long long ll;
string s[maxn];
int Trie[10*maxn][26],tot=1;
ll d[26],D[maxn],n,m;
struct Node{
ll cnt,dif;
Node(){ cnt=0; dif=0; };
}node[maxn*10];
void insert(int id){
int len=s[id].size();
int p=0; ll dif=1;
for(int i=0;i<len;++i){
int k=s[id][i]-'a';
if(Trie[p][k]==0)
Trie[p][k]=tot++;
p=Trie[p][k];
dif*=d[k];
dif%=m;
node[p].cnt++; node[p].dif=dif;
}
D[id]=dif;
}
ll query(int id){
int len=s[id].size();
int p=0; ll ans=0;
for(int i=0;i<len;++i){
int k=s[id][i]-'a';
p=Trie[p][k];
if(node[p].dif>D[id])
ans+=node[p].cnt;
}
return ans;
}
int main(){
cin>>n>>m;
for(int i=0;i<26;++i) cin>>d[i];//scanf("%lld",&d[i]);
for(int i=1;i<=n;++i){
cin>>s[i];
insert(i);
}
for(int i=1;i<=n;++i){
cout<<query(i);//printf("%lld",query(i));
if(i!=n) putchar(' ');
else putchar('\n');
}
return 0;
}
K.A Good Game(树状数组+贪心)
题意:
给定数组a,每次的区间求和得到数组b,把b由大到小排序,分别乘以n,n-1,······,1,得到最大值。
分析:
使用树状数组,求和,排序,求最大值。
代码:
#include<cstdio>
#include<string>
#include<cstring>
#include<math.h>
#include<stack>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=100010;
int bit[MAXN+1],n,m;
ll sum(int i){
ll s=0;
while(i>0){
s+=bit[i];
i-=i&-i;
}
return s;
}
void add(int i,int x){
while(i<=n){
bit[i]+=x;
i+=i&-i;
}
}
ll s[MAXN+1],ans;
int main(){
int T,t,l,r;
scanf("%d",&T);
while(T--){
memset(bit,0,sizeof(bit));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&t);
add(i,t);
}
for(int i=1;i<=m;i++){
scanf("%d%d",&l,&r);
s[i]=sum(r)-sum(l-1);
}
sort(s+1,s+m+1);
ans=0;
for(int i=m;i>0;i--){
ans+=s[i]*i;
}
printf("%lld\n",ans);
}
return 0;
}