ACM-ICPC 2018 徐州賽區網絡預賽(solve7/11)

題目鏈接:https://www.jisuanke.com/contest/1557?view=challenges

A題

題意:給你2^k個面具,然後n個人,要求挨着的2個人二進制之後不能互補。問你有多少不同的方案數

思想:對於這2^k個數最多就兩個數是互補的,所以考慮下第一個數有2^k種選擇,第二個2^k-1種,假如倒數第二個跟第一個不一樣,那麼最後一個由2^k-2種選擇,假如一樣的話,那麼就可以將序列縮短到n-2,繼續遞歸下去計算。

Ps:對於第一個題,可以推那個容斥的公式,或者看下這個DP寫的非常好:http://www.cnblogs.com/565261641-fzh/p/9616556.html

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod =1e9+7;
const int maxn=1e6+5;
ll a[maxn];
void init()
{
    a[0]=1;
    for(int i=1;i<maxn;i++)
		a[i]=(a[i-1]*2ll)%mod;
}
ll Pow(ll n, ll m)
{
    ll ans=1;
    while(m)
    {
        if(m&1)
            ans=(ans*n)%mod;
    	n=(n*n)%mod;
        m=m>>1;
    }
    return ans%mod;
}
ll check(ll n,ll k)
{
	if(n==1)//只有一個人
        return a[k]%mod;
    else if(n==2)//只有兩個人
        return (a[k]*(a[k]-1))%mod;
    ll ans;
    ans=((a[k]*Pow((a[k]-1),n-2)%mod)*(max(a[k]-2ll,0ll)))%mod;
    ans=(ans+check(n-2,k)%mod)%mod;
    return ans;
}
int main()
{
    init();
    int t;
    scanf("%d",&t);
    while(t--)
    {
     	ll  n,k;
        scanf("%lld%lld",&n,&k);
        ll T=check(n,k);
        printf("%lld\n",T);
    }
    return 0;
}

B題

題意:兩個人玩遊戲,有n個輪次,初始分數爲m,每個輪次有三種選擇,A:將m增加a,B:將m減少b,C:可以將m取 相反數。只能選擇一個對m進行操作。K玩家想要使最終分數大於等於k,S玩家想要使最終分數小於等於l,K玩家 先手,問最終K玩家能否使分數大於等於k。

思想:記憶化搜索(玄學時間複雜度),先將m加100,這樣就不用考慮負數的情況了,但是判斷還得需要減去100.

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3+5;
int n,m,k,l;
int dp[maxn][205];//將複數的放上邊 
int a[maxn];
int b[maxn];
int c[maxn]; 
int dfs(int id,int num)
{
	if(id>n)
		return num-100;
	if(dp[id][num]!=(1<<29))
		return dp[id][num];
	if(id%2==1)//因爲從1開始 所以第一個是max 
	{
		int temp=-100;//尋找這個操作的最大值 
		if(a[id])//對於每種可能都去搜索一遍 
		{
			int Temp=min((int)100,num-100+a[id]);//num加了100 所以需要減去100 
			temp=max(temp,dfs(id+1,Temp+100));
		}
		if(b[id])
		{
			int Temp=max((int)-100,num-100-b[id]);
			temp=max(temp,dfs(id+1,Temp+100));
		}
		if(c[id])
		{
			int Temp=num-100;
			temp=max(temp,dfs(id+1,-Temp+100));
		}
		dp[id][num]=temp;
		return temp; 
	}
	else//min 
	{
		int temp=100;//尋找這個可能的最小值 
		if(a[id])
		{
			int Temp=min((int)100,num-100+a[id]);
			temp=min(temp,dfs(id+1,Temp+100));
		}	
		if(b[id])
		{
			int Temp=max((int)-100,num-100-b[id]);
			temp=min(temp,dfs(id+1,Temp+100)); 
		}
		if(c[id])
		{
			int Temp=num-100;
			temp=min(temp,dfs(id+1,-Temp+100));
		}
		dp[id][num]=temp;
		return temp;
	}
}
int main()
{
	for(int i=0;i<maxn;i++)
		for(int j=0;j<205;j++)
			dp[i][j]=1<<29;
	scanf("%d%d%d%d",&n,&m,&k,&l);
	for(int i=1;i<=n;i++)
		scanf("%d%d%d",&a[i],&b[i],&c[i]);	
	int temp = dfs(1,m+100);
	if(temp>=k)
		printf("Good Ending\n");
	else if(temp<=l)
		printf("Bad Ending\n");
	else
		printf("Normal Ending\n");
	return 0;
} 

 

F題

題意:給你n幀畫面,問你最長的連續點的長度是多少,就是對於i中有(1,1)下一秒還有 那麼長度就2,也就說答案要不是0要不就大於2;

思想 map搞一下,模擬那個過程即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
const int INF = 0x3f3f3f3f;
struct point{
    int x, y;
}a[100005];
int ans[100005];
map<pair<int, int> , int>mp, mpp;

int main()
{
    int T, n, c, maxx;
    scanf("%d", &T);
    while(T--){
        mp.clear();
        scanf("%d", &n);
        maxx = 0;
        for(int i = 0; i < n; i++){
            scanf("%d", &c);
            for(int j = 0; j < c; j++){
                scanf("%d%d", &a[j].x, &a[j].y);
                pair<int, int> p(a[j].x, a[j].y);
                if(!mpp[p]){
                    mpp[p] = 1;
                    mp[p]++;
                }
                int temp = mp[p];
                ans[j] = temp;
                if(temp > maxx)
                    maxx = temp;
            }
            mpp.clear();
            mp.clear();
            for(int j = 0; j < c; j++){
                pair<int, int> p(a[j].x, a[j].y);
                mp[p] = ans[j];
            }
        }
        if(maxx > 1)
            printf("%d\n", maxx);
        else
            printf("0\n");
    }
    return 0;
}

G題

題意:就是給你n次衝浪形成的一個矩陣,每次都會留下一個痕跡,但是也有可能會毀掉原來留下的痕跡,問你痕跡的長度是多少。

思想:兩個樹狀數組,維護X和Y的最大值,從後往前,因爲前邊有可能被後邊的幹掉。發現對於某次海浪,改變的值等於比當前X大的的所有X裏面的Y最大值和當前Y的差值,X同理。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=50005;
ll x[maxn];
ll y[maxn];
ll Qx[maxn*200];
ll Qy[maxn*200];
void add(int x,ll valu)
{
	for(ll i=x;i>0;i-=(i&(-i)))
		Qy[i]=max(Qy[i],valu);
}
void Add(int x,ll valu)
{
	for(ll i=x;i>0;i-=(i&(-i)))
		Qx[i]=max(Qx[i],valu);	
}
ll query(ll x)
{
	ll ans=0;
	for(ll i=x;i<maxn*200;i+=(i&(-i)))
		ans=max(ans,Qy[i]);	
	return ans;
}
ll Query(ll x)
{
	ll ans=0;
	for(ll i=x;i<maxn*200;i+=(i&(-i)))
		ans=max(ans,Qx[i]);
	return ans;
}
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=0;i<n;i++)
		scanf("%lld%lld",&x[i],&y[i]);
	ll sum=0;
	for(int i=n-1;i>=0;i--)
	{
		ll temp=query(x[i]);//比X[i]大的最大的y 
		sum=sum+y[i]-temp;
		temp=Query(y[i]);//比y[i]大的最大的x 
		sum=sum+x[i]-temp;
		
		add(x[i],y[i]);
		Add(y[i],x[i]);
	//	printf("%lld\n",sum);
	}
	printf("%lld\n",sum);
	return 0;
}
 

H題

題意:有n個數,q次詢問,每次不是問區間那個式子的值,要不就是改變某點的值。

思想:樹狀數組,一個建立a[i] 一個建立i*a[i] ,維護即可,爲什麼這樣 自己畫畫,就OK了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int Maxn=1e5+5;
ll a[Maxn];
ll b[Maxn];
ll c[Maxn];
int n,q;
void add(int x,ll num)
{
	while(x<=n)
	{
		b[x]+=num;
		x+=(x&(-x));
	}
}
void Add(int x,ll num)
{
	while(x<=n)
	{
		c[x]+=num;
		x+=(x&(-x));
	}
}
ll query(int x)
{
	ll ans=0;
	while(x)
	{
		ans+=b[x];
		x-=(x&(-x)); 
	}
	return ans;
}
ll Query(int x)
{
	ll ans=0;
	while(x)
	{
		ans+=c[x];
		x-=(x&(-x));
	}
	return ans;
}
int main()
{
	scanf("%d%d",&n,&q);
	for(ll i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		add(i,a[i]);
		Add(i,i*a[i]);
	}
	while(q--)
	{
		int op,x;
		ll y;
		scanf("%d%d%lld",&op,&x,&y);
		if(op==1)
		{
			ll temp=(y+1)*(query(y)-query(x-1));
			ll Temp=temp-(Query(y)-Query(x-1));
			printf("%lld\n",Temp);
		}
		else
		{
			add(x,-a[x]);
			Add(x,-x*a[x]);
			a[x]=y;
			add(x,a[x]);
			Add(x,x*a[x]);
		} 
	}
	return 0;
} 

I題

題意:給你個單詞和字符串,對於字符串的每個單詞可以通過已給的方式得到一個2位數,然後拼接len*2個數字,去掉前導0多長。

思想:模擬

#include<bits/stdc++.h>
using namespace std;
char str[1000005];
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{	
		int n,ans=0;
		char c;
		scanf("%d %c",&n,&c);
		scanf("%s",str);
		int len=strlen(str);
		vector<int>V;
		for(int i=0;i<len;i++)
		{
			int temp=abs((int)(c)-str[i]);
			int T=temp%10;
			temp=temp/10;
			temp=temp%10;
			V.push_back(temp);
			V.push_back(T);
		}
		for(int i=0;i<V.size();i++)
		{
			if(V[i]==0)
				ans++;
			else
				break;	
		}
		printf("%d\n",max(1,(int)V.size()-ans));
	}
	return 0;
}

J題

題意:給你一個n*m的格子,每個點可以往右和往下連邊,給你對應的連邊的權值,問你最少建立多少花費的邊使所有點之間都可以互相達到,並且保證路徑是唯一的,問你這樣花費最小是多少。

思想:正着想不好想,所以就反過來想,怎麼花費最小,就是讓構成一個樹花費最大即可,就變成了求最大生成樹,然後樹上兩點的距離就變成了求LCA即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn= 260005;
int n,m,cnt;
int head[maxn];
int fa[maxn];
int dep[maxn];
ll dist[maxn];
int pre[maxn][30];
struct node{
	int u;
	int v;
	ll va;
	bool operator < (const node &a)
	{
		return va > a.va;
	}
}no[maxn<<1];
struct Node{
	int v;
	int next;
	ll valu;
}No[maxn<<2];
void add(int u,int v,ll w)
{
	No[cnt].v=v;
	No[cnt].valu=w;
	No[cnt].next=head[u];
	head[u]=cnt++;
}
int _id(int x,int y)
{
	return (x-1)*m+y;
}
int find(int x)
{
	if (x != fa[x]) 
		fa[x] = find(fa[x]);
    return fa[x];
}
void dfs(int u,int fa,int deep)
{
	dep[u]=deep;
	pre[u][0]=fa;
	for(int i=head[u];i!=-1;i=No[i].next)
	{
		int v=No[i].v;
		if(v==fa)
			continue;
		if(dist[v]==0)
		{
			dist[v]=dist[u]+No[i].valu;
			dfs(v,u,deep+1);
		}
	}
}
void __ST()
{
	for(int j=1;(1<<j)<=n*m;j++)
		for(int i=1;i<=n*m;i++)
			pre[i][j]=pre[pre[i][j-1]][j-1];
}
int lca(int x, int y)
{
    if(dep[x] < dep[y]) 
		swap(x, y);
    int temp = 0;
    while((1<<temp) <= dep[x])
		temp++;
    temp--;
    for(int i = temp;i >= 0;i--)
    {
        if(dep[x] - (1<<i) >= dep[y])
            x = pre[x][i];
    }
    if(x == y) 
		return x;
    for(int i = temp;i >= 0;i--)
    {
        if( pre[x][i] != pre[y][i])
        {
            x = pre[x][i];
			y = pre[y][i];
		} 
    }
    return pre[x][0];
}
ll Getdist(int x,int y)
{
	return dist[x]+dist[y]-2*dist[lca(x,y)];
} 
int main()
{
	char a[5],b[5];
	ll c,d;
	cnt=0;
	memset(head,-1,sizeof(head));
	scanf("%d%d",&n,&m);	
	for(int i=0;i<=n*m;i++)
		fa[i]=i;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			scanf("%s%lld%s%lld",a,&c,b,&d);
			no[cnt++]=node{_id(i,j),_id(i+1,j),c};
			no[cnt++]=node{_id(i,j),_id(i,j+1),d};	
		}
	}
	sort(no,no+cnt);
	int Count=0;
	for(int i=0;i<cnt;i++)
	{
		int fx=find(no[i].u);
		int fy=find(no[i].v);
		if(fx!=fy)
		{
			add(no[i].u,no[i].v,1ll);
			add(no[i].v,no[i].u,1ll);
			fa[fx]=fy;
			if(++Count == n*m-1)
				break;
		}
	}
	dist[1]=1;
	dfs(1,0,1);//u fa deep 
	__ST();
	scanf("%d",&c);
	while(c--)
	{
		int x1,y1,x2,y2;
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		ll LCA = Getdist(_id(x1,y1),_id(x2,y2));
		printf("%lld\n",LCA);
	}
	return 0;
}

 

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