1. 程式人生 > 其它 >noip模擬19[u·v·w](雅禮集訓)

noip模擬19[u·v·w](雅禮集訓)

\(noip模擬19\;solutions\)

我我我我我我只有49pts,我要氣死了

狀態也不算太好,畢竟當時是改著改著上一場的最後一題就開始考試了

而且當是我極其憤怒,根本改不過

·

\(T1\;u\)

就這個題,我在考場上又是手動給我的程式加了個log,然後別人都那61pts,只有我自己46pts

但是這個題正解確實在我的意識範圍內,就是一個小小的差分,但是這個差分非常的叼鑽

首先你得知道,還有一種斜著的差分,用式子表示就是\(f[i][j]=f[i-1][j-1]+f[i-1][j]-f[i-2][j-1]\)

畫個圖理解一下:

你在左上角加上一個1,我們利用差分的思想的話,直接就吧這個紫色的區域都加上了2

那要是下面加多了怎麼辦,在你要加的那個三角形的右下角的地方-1,

但是你發現好像還是多了,在綠色的地方加上一,黃色的地方減去一,我們要處理的是粉色的區域

那麼你會發現,好像還有一塊紫色多出來,那我們就直接利用正常的二維差分來解決

我們在黃色的地方再減1,右下角再加一,注意這次就用正常的差分就好了

\(f[i][j]=f[i-1][j]+f[i][j-1]-f[i-1][j-1]\)

這樣的話,這個題就直接解決了,然後就維護兩個差分陣列就好了,

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define ll long long
const int N=1e3+5;
const int Q=3e5+5;
int n,q;
ll jz[N][N],ans,sum[N][N];
void adda(int x,int y,int v){
	if(x<=n&&y<=n)jz[x][y]+=v;
}
void addb(int x,int y,int v){
	if(x<=n&&y<=n)sum[x][y]+=v;
}
signed main(){
	scanf("%d%d",&n,&q);
	for(re i=1,r,l,c,s;i<=q;i++){
		scanf("%d%d%d%d",&r,&c,&l,&s);
		adda(r+l,c,-s);
		adda(r+l,c+l,s);
		addb(r,c,s);
		addb(r+l,c+l,-s);
	}
	for(re i=1;i<=n;i++){
		for(re j=1;j<=n;j++){
			jz[i][j]+=jz[i-1][j]+jz[i][j-1]-jz[i-1][j-1];
			sum[i][j]+=sum[i-1][j-1]+sum[i-1][j];
			if(i>1)sum[i][j]-=sum[i-2][j-1];
			ans^=(jz[i][j]+sum[i][j]);
		}	
	}
	printf("%lld",ans);
}

·

\(T2\;v\)

狀壓!!!

對一個裸的狀壓,然後用map維護,直接記憶化搜尋,一點技術含量都沒有

就是要注意那個倒位置的時候仔細點,卡常過,特判過,我特判過的

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define pa pair<int,int>
const int N=32;
int n,k;
char a[N];
int bas;
map<int,double> mp[15];
double q[24][1<<21];
double dfs(int s,int cz){
	if(cz>k)return 0.0;
	int len=n-cz+1;
	if(len<=21&&q[len][s]!=-1)return q[len][s];
	if(len>21&&mp[len-21].find(s)!=mp[len-21].end())return mp[len-21][s];
	double ans=0;
	for(re i=1;i<=len/2;i++){
		int tmp1=((s>>i)<<i-1)|(s&((1<<i-1)-1));
		int cho1=(s>>i-1)&1;
		int x=len-i+1;
		int tmp2=((s>>x)<<x-1)|(s&((1<<x-1)-1));
		int cho2=(s>>x-1)&1;
		//if(s==bas)cout<<i<<" "<<tmp1<<" "<<cho1<<"    "<<tmp2<<" "<<cho2<<endl;
		ans+=2.0*max(dfs(tmp1,cz+1)+cho1*1.0,dfs(tmp2,cz+1)+cho2*1.0)/(len*1.0);
	}
	if(len&1){
		int mid=len+1>>1;
		int tmp=((s>>mid)<<mid-1)|(s&((1<<(mid-1))-1));
		int cho=(s>>mid-1)&1;
		ans+=(dfs(tmp,cz+1)+cho*1.0)/(len*1.0);
	}
	if(len<=21)q[len][s]=ans;
	else mp[len-21][s]=ans;
	return ans;
}
signed main(){
	scanf("%d%d",&n,&k);
	scanf("%s",a+1);
	for(re i=1;i<=21;i++)
		for(re j=0;j<(1<<21);j++)
			q[i][j]=-1.0;
	int c=0,b=0;
	for(re i=1;i<=n;i++){
		if(a[i]=='W')
			bas|=(1<<i-1),c++;
		else b++;
	}
	if(c==b&&(k==n||k==n-1))printf("%.10lf",n/2.0);
	else printf("%.10lf",dfs(bas,1));
}

·

\(T3\;w\)

這個題吧我本來是想dfs的但是後來發現我根本無法得到我要的狀態,也根本無法得到轉移方程

然後我就對著那個1分的點就上去了

考完後我看題解,有這麼一個性質:

設我們要翻轉的邊集為S,那麼我們的最少的路徑條數就是這個邊集所能連線的點中度數為奇數的點的一半,

一開始我還不信,後來就去手動模擬,然後試了好幾組,都對了

其實是那些可以配對為偶數的邊都可以連成一條,所以只有奇數點才為路徑的端點而且需要除以2

這樣我們就會有一個狀態了,狀態中有兩個量,一個是路徑條數,一個是邊的數量

我們設f[i][1/0]表示i和他父親的連邊(1翻轉,0不翻)為1/0時的最小路徑條數,翻轉邊數

注意這裡的狀態包括和父親的連邊以及i的子樹,轉移就好了

AC_code
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=1e5+5;
const int inf=0x3f3f3f3f;
int n;
int to[N*2],nxt[N*2],val[N*2],head[N],rp;
void add_edg(int a,int b,int c,int d){
	to[++rp]=b;
	if(c==d)val[rp]=0;
	else if(d==2)val[rp]=2;
	else val[rp]=1;
	nxt[rp]=head[a];
	head[a]=rp;
}
struct node{
	int sum,len;
	node(){}
	node(int x,int y){this->sum=x;this->len=y;}
	node operator + (node x)const {
		node ret;
		ret.sum=sum+x.sum;
		ret.len=len+x.len;
		return ret;
	}
	bool operator < (node x)const {
		if(sum!=x.sum)return sum<x.sum;
		return len<x.len;
	}
	void print(){
		printf("%d %d\n",sum,len);
	}
}dp[N][2];
void dfs(int x,int f,int typ){
	node t1(inf,inf),t2(0,0),n1,n2;
	for(re i=head[x];i;i=nxt[i]){
		int y=to[i];
		if(y==f)continue;
		dfs(y,x,val[i]);
		n1=min(t1+dp[y][0],t2+dp[y][1]);
		n2=min(t1+dp[y][1],t2+dp[y][0]);
		t1=n1,t2=n2;
		//t1.print();t2.print();
	}
	if(typ==0){
		dp[x][0]=min(node(t1.sum+1,t1.len),t2);
		dp[x][1]=node(inf,inf);
	}
	else if(typ==1){
		dp[x][0]=node(inf,inf);
		dp[x][1]=min(node(t1.sum,t1.len+1),node(t2.sum+1,t2.len+1));
	}
	else {
		dp[x][0]=min(node(t1.sum+1,t1.len),t2);
		dp[x][1]=min(node(t1.sum,t1.len+1),node(t2.sum+1,t2.len+1));
	}
}
signed main(){
	/*node x(1,2),y(1,3);
	node z=min(x,y);
	cout<<z.sum<<" "<<z.len<<endl;*/
	scanf("%d",&n);
	for(re i=1,a,b,c,d;i<n;i++){
		scanf("%d%d%d%d",&a,&b,&c,&d);
		add_edg(a,b,c,d);
		add_edg(b,a,c,d);
	}
	dfs(1,0,0);
	printf("%d %d",dp[1][0].sum/2,dp[1][0].len);
}