CF 365 div 2 (線段樹應用/dp)

比賽網址

D

題意是對於一個序列,有一些詢問,詢問一個區間裏出現了偶數次的數字的異或和。

離線回答,應用線段樹即可

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<set>
#include<math.h>
#include<queue>
#include<map>
#include<stack>
#include<deque>
#define go(i,a,b) for (int (i)=(a);(i)<=(b);(i)++)
#define ll long long
#define N 1000005
using namespace std;
struct query{
	int l,r,i,ans;
}q[N];
struct node{
	int x,i,p;
}a[N];
int t[4*N],n,m,x[N],cnt=0,has[N];
map<int,int>mp;
bool cmp1(query a, query b){ return a.r<b.r; }
bool cmp2(query a, query b){ return a.i<b.i; }
bool cmp3(node a, node b){ return a.x<b.x; }
bool cmp4(node a, node b){ return a.i<b.i; }
void init(){
	scanf("%d",&n);
    for(int i=1;i<=n;++i){
    	scanf("%d",&a[i].x);
    	a[i].i=i;
    	x[i]=x[i-1]^a[i].x;  //xor 前綴和 
	}
	sort(a+1,a+1+n,cmp3);
	for (int i=1;i<=n;i++){
		if (a[i].x==a[i-1].x) a[i].p=cnt;
		else a[i].p=++cnt;
	}
	sort(a+1,a+1+n,cmp4);
    scanf("%d",&m);
    for(int i=1;i<=m;++i){
        scanf("%d%d",&q[i].l,&q[i].r);
        q[i].i=i;
    }
}
void updata(int k){
	t[k]=t[k*2]^t[k*2+1];
}
void insert(int k, int l, int r, int x,int c){
	if (l>x||r<x) return;
	if (x==r&&x==l){
		t[k]=c;
		return;
	}
	int m=(l+r)/2;
	insert(k*2,l,m,x,c);insert(k*2+1,m+1,r,x,c);
	updata(k);
}
int query(int k, int l, int r, int left, int right){
	if (l>right||r<left) return 0;
	if (left<=l&&r<=right){
		return t[k];
	}
	int m=(l+r)/2;
	return query(k*2,l,m,left,right)^query(k*2+1,m+1,r,left,right);
}
void solve(){
	sort(q+1,q+m+1,cmp1);
	memset(has,-1,sizeof(has));
	int r=0;
	for (int i=1;i<=m;i++){
		while (r<q[i].r){
			r++;
			if (has[a[r].p]!=-1){
				insert(1,1,n,has[a[r].p],0);
			}
			has[a[r].p]=r;
			insert(1,1,n,r,a[r].x);
		}
		q[i].ans=query(1,1,n,q[i].l,q[i].r)^x[q[i].r]^x[q[i].l-1];
	}
	sort(q+1,q+1+m,cmp2);
	for (int i=1;i<=m;i++){
		printf("%d\n",q[i].ans);
	}
}
int main(){
	init();
	solve();
}

E

題意是:對於一個數字,給出一個序列,求一個最短的子序列使得這個子序列的積是這個數字的倍數

如果有很多個這樣的子序列輸出和最小的那個

做法:預處理k的約數,然後離散化進行dp就可以了(約數並沒有那麼多)

輸出方案這個地方wa了好幾次,然後又忘記特判1……

#include<iostream>
#include<map> 
#include<algorithm>
#include<string.h>
#include<math.h>
#define ll long long 
using namespace std;
ll num[10005],a[1005], ai[1005];
int dp[2][10005],p[1005][10005];
ll sum[2][10005];

int tot=0;
map<ll, int> mp;

ll gcd(ll a, ll b) {
	return b==0?a:gcd(b,a%b);
}
void init(ll k){
	for (ll i=1;i<=sqrt((double)k+0.5);i++){
		if (k%i==0){
			num[++tot]=i;
			if (i*i!=k) num[++tot]=k/i;
		}
	}
	sort(num+1,num+1+tot);
	//cout<<tot<<endl;
	for(int i=1;i<=tot;i++) mp[num[i]]=i; 
}
int main(){
	
	ll n,k;
	cin>>n>>k;
	init(k);
	ll minn,tag;
	for (int i=1;i<=n;i++){
		cin>>a[i];
		ai[i]=gcd(a[i],k);
		if (i==1) minn=a[i],tag=1;
		else{
			if (minn>a[i]) minn=a[i],tag=i;
		}
	}
	if (k==1){
		cout<<1<<endl<<tag<<endl;
		return 0;
	}
	memset(dp,0x3f,sizeof(dp));
	memset(sum,0x3f,sizeof(sum));
	memset(p,0,sizeof(p));
	dp[0][1]=0;
	for (int i=1;i<=n;i++){
		for (int j=1;j<=tot;j++){ //第j個約數
			int now=i%2;
			int tmp=gcd(ai[i],num[j]); //最大貢獻多少
			int x=mp[num[j]/tmp];
			dp[now][j]=dp[1-now][j];
			sum[now][j]=sum[1-now][j];
			p[i][j]=j;
			if (dp[1-now][x]+1<dp[now][j]){
				dp[now][j]=dp[1-now][x]+1;
				sum[now][j]=sum[1-now][x]+a[i];
				p[i][j]=x;
			}
			else if (dp[1-now][x]+1==dp[now][j]&&
					 sum[1-now][x]+a[i]<sum[now][j]){
				sum[now][j]=sum[1-now][x]+a[i];
				p[i][j]=x;
			}
		}
	}
	
	if (dp[n%2][tot]==1061109567) cout<<"-1"<<endl;
	else {
		cout<<dp[n%2][tot]<<endl;
		int tmp=tot, j=n;
		for (int i=1;i<=dp[n%2][tot];i++){
			for (;j>=1;j--){
				if (p[j][tmp]!=tmp){
					cout<<j<<" ";
					tmp=p[j][tmp];
					j--; 
					break;
				}
			}
		}
	}
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章