目錄
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;
}