Codeforces Round #597 (Div. 2)

A. Good ol’ Numbers Coloring
判斷2個數是否互質就行了

AC代碼

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int man = 2e5+10;
#define IOS ios::sync_with_stdio(0)
template<typename T>T gcd(T a, T b) {return b==0?a:gcd(b, a%b);}
template<typename T>T exgcd(T a,T b,T &g,T &x,T &y){if(!b){g = a,x = 1,y = 0;}else {exgcd(b,a%b,g,y,x);y -= x*(a/b);}}
#ifndef ONLINE_JUDGE
#define debug(fmt, ...) {printf("debug ");printf(fmt,##__VA_ARGS__);puts("");}
#else
#define debug(fmt, ...)
#endif
typedef long long ll;
const ll mod = 1e9+7;
 
int main() {
	#ifndef ONLINE_JUDGE
		//freopen("in.txt", "r", stdin);
		//freopen("out.txt","w",stdout);
	#endif
	int t;cin >> t;
	while(t--){
		int a,b;
		cin >> a>> b;
		if(gcd(a,b)!=1)cout << "Infinite" << endl;
		else cout << "Finite" <<endl;
	}
	return 0;
}

B. Restricted RPS
題意:石頭剪刀布的規則,給你一個字符串只包含‘R’,‘P’,'S’三種字符,
R:石頭 P:布 S:剪刀
然後有a個石頭,b個布,c個剪刀
讓你贏的局數大於等於ceil(n/2)(向上取整)

思路:一次匹配,有能贏的就贏,不能贏的先放着不管,最後在把剩餘的隨便分給先前沒管的。

AC代碼

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int man = 2e5+10;
#define IOS ios::sync_with_stdio(0)
template<typename T>T gcd(T a, T b) {return b==0?a:gcd(b, a%b);}
template<typename T>T exgcd(T a,T b,T &g,T &x,T &y){if(!b){g = a,x = 1,y = 0;}else {exgcd(b,a%b,g,y,x);y -= x*(a/b);}}
#ifndef ONLINE_JUDGE
#define debug(fmt, ...) {printf("debug ");printf(fmt,##__VA_ARGS__);puts("");}
#else
#define debug(fmt, ...)
#endif
typedef long long ll;
const ll mod = 1e9+7;
map<char,int>mp;
map<char,char>dui;
 
int main() {
	#ifndef ONLINE_JUDGE
		//freopen("in.txt", "r", stdin);
		//freopen("out.txt","w",stdout);
	#endif
	int t;cin >>t;
	dui['R'] = 'P';dui['P'] = 'S';dui['S'] = 'R'; 
	while(t--){
		int n;cin >> n;
		int a,b,c;
		cin >> a>> b >> c;
		mp['R'] = a;mp['P'] = b;mp['S'] = c;
		string s;cin >> s;
		int ans = 0;
		char res[110];
        memset(res,0,sizeof(res));//注意每次對res清零,無腦貢獻2發罰時.
		for(int i = 0;i < s.size();i++){
			if(mp[dui[s[i]]]>0){
				ans++;
				mp[dui[s[i]]]--;
				res[i] = dui[s[i]];
			}
		}
		for(int i = 0;i < n;i++){
			if(res[i]!='R'&&res[i]!='S'&&res[i]!='P'){
				if(mp['S']>0)res[i] = 'S',mp['S']--;
				else if(mp['R']>0)res[i] = 'R',mp['R']--;
				else if(mp['P']>0)res[i] = 'P',mp['P']--;
			}
		}
		res[n] = '\0';
		n = ceil(1.0*n/2);
		if(ans>=n)cout << "YES\n",cout << res << endl;
		else cout << "NO\n";
	}
	return 0;
}

C. Constanze’s Machine
題意:原序列中的m會變成nn,w會變成vv;形成最終序列
現在給你最終序列,求原序列有多少種。

思路:首先要組成m或w,肯定要連續的nn或vv,那我們就來看長度爲n的vv有多少種情況。
考慮dp,dp[i]表示前i個位置的組合情況,對於當前位置,要麼自己單獨,要麼與前一個n組成一個m,所以dp[i] = dp[i-1] + dp[i-2] 。dp[0] = 1,dp[1] = 1;
(其實列舉幾個出來就會發現就是一個fib數列)

每段連續的求出來了,乘起來就是答案,注意取模.

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int man = 2e5+10;
#define IOS ios::sync_with_stdio(0)
template<typename T>T gcd(T a, T b) {return b==0?a:gcd(b, a%b);}
template<typename T>T exgcd(T a,T b,T &g,T &x,T &y){if(!b){g = a,x = 1,y = 0;}else {exgcd(b,a%b,g,y,x);y -= x*(a/b);}}
#ifndef ONLINE_JUDGE
#define debug(fmt, ...) {printf("debug ");printf(fmt,##__VA_ARGS__);puts("");}
#else
#define debug(fmt, ...)
#endif
typedef long long ll;
const ll mod = 1e9+7;
int dp[man];
void init(int n){
	dp[1] = dp[0] = 1;
	for(int i = 2;i <= n;i++){
		dp[i] = (dp[i-1] + dp[i-2])%mod;
	}
}
vector<int>res;
 
int main() {
	#ifndef ONLINE_JUDGE
		//freopen("in.txt", "r", stdin);
		//freopen("out.txt","w",stdout);
	#endif
	string s;
	cin >> s;
	init(s.size());
	int len = s.size();
	bool f = 0;
	for(int i = 0;i < len;i++){
		if(s[i]=='w'||s[i]=='m')f = 1;
		if(s[i]=='u'||s[i]=='n'){
			int le = 1;int j;
			for(j = i+1;j < len;j++){
				if(s[j]==s[i])le++;
				else break;
			}
			i = j-1;
			res.push_back(le);
		}
	}
	if(f)cout << "0" << endl;
	else{
		ll ans = 1;
		for(int i = 0;i < res.size();i++){
			ans = (ans*dp[res[i]])%mod;
		}
		cout << ans << endl;
	}
	return 0;
}

D. Shichikuji and Power Grid
題意:n個笛卡爾座標上的點,每個點有一個ci,ki,現在需要全部點通電,可以在該點建發電站,也可以連接其他發電站,在每個點建發電站的花費爲ci,連接其他點的花費爲兩點的曼哈頓距離*兩點k之和。求花費最小。

思路:建圖,跑最小生成樹。爲什麼這樣做?首先我們每個點都要通電且花費最小,很明顯最小生成樹。
建圖:一個超級源點連接n個點,邊權爲ci,表示這個點是否做發電站,然後每個點與其他點連一條邊,權值爲連接邊的花費。
用prime算法跑生成樹,結構體記錄當前點的前驅結點,用來保存答案

AC代碼

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int man = 2e3+10;
#define IOS ios::sync_with_stdio(0)
template<typename T>T gcd(T a, T b) {return b==0?a:gcd(b, a%b);}
template<typename T>T exgcd(T a,T b,T &g,T &x,T &y){if(!b){g = a,x = 1,y = 0;}else {exgcd(b,a%b,g,y,x);y -= x*(a/b);}}
#ifndef ONLINE_JUDGE
#define debug(fmt, ...) {printf("debug ");printf(fmt,##__VA_ARGS__);puts("");}
#else
#define debug(fmt, ...)
#endif
typedef long long ll;
ll inf = 1e18;
const ll mod = 1e9+7;
struct node{
    int x,y;
    int c,k;
}a[man];

struct noe{
    int u;
    ll dis;
};
struct no{
    int u,pre;
    ll dis;
    bool operator <(const no &a)const{
        return dis > a.dis;
    }
};
vector<noe>sp[man];
ll dis[man];
bool vis[man];
priority_queue<no>q;
vector<pair<int,int> >res2;
vector<int>res1;
int n;   

void prim(int s){
    for(int i = s;i <= n;i++)dis[i] = inf;
    memset(vis,false,sizeof(vis));
    q.push(no{s,-1,0});
    ll ans = 0;
    while(!q.empty()){
        no tp = q.top();q.pop();
        int u = tp.u;
        if(vis[u])continue;
        vis[u] = 1;
        ans += tp.dis;
        if(tp.dis!=0){
            if(tp.pre==0)res1.push_back(u);
            else res2.push_back(make_pair(u,tp.pre));
        }
        for(int i = 0;i < sp[u].size();i++){
            int v = sp[u][i].u;
            ll diss = sp[u][i].dis;
            if(vis[v])continue;
            if(dis[v] > diss){
                dis[v] = diss;
                q.push(no{v,u,dis[v]});
            }
        }
    }
    cout << ans << endl;
    cout << res1.size() << endl;
    for(int i = 0;i < res1.size();i++){
        cout << res1[i] << " " ;
    }cout <<endl;
    cout << res2.size() << endl;
    for(int i = 0;i < res2.size();i++){
        cout << res2[i].first << " " << res2[i].second <<endl;
    }
}

signed main() {
    #ifndef ONLINE_JUDGE
        //freopen("in.txt", "r", stdin);
        //freopen("out.txt","w",stdout);
    #endif
    
    cin >> n;
    for(int i = 1;i <= n;i++)cin >> a[i].x >> a[i].y;
    for(int i = 1;i <= n;i++)cin >> a[i].c;
    for(int i = 1;i <= n;i++)cin >> a[i].k;
    int s = 0;
    for(int i = 1;i <= n;i++){
        sp[s].push_back(noe{i,a[i].c});
        sp[i].push_back(noe{s,a[i].c});
    }
    for(int i = 1;i <= n;i++){
        for(int j = i + 1;j <= n;j++){
            ll dis = abs(a[i].x-a[j].x) + abs(a[i].y-a[j].y);
            sp[i].push_back(noe{j,1ll*dis*(a[i].k + a[j].k)});
            sp[j].push_back(noe{i,1ll*dis*(a[i].k + a[j].k)});
        }
    }
    prim(s);
    return 0;
}

E:
題意:給你一個10*10的網格,要你從左下角走到左上角,奇數行只能向右走,偶數行向左走,遇到邊界就向上。
每次的走的步數是由投擲骰子決定的,6種可能,概率相同,還有直達的,就是向上x行,但不能2次都連續2次直達,
問最少步數。

思路:很明顯的一個期望dp,比賽時時間不夠(還是太菜),考慮把二維格子想成一條直線,然後樓梯關係對應一下,
dp[i]表示離終點的最小期望步數,dp[100] = 0,
對於94-99情況比較特殊,因爲會有自環,
dp[i] = (dp[i]+1)*5/6 + (dp[100] + 1)/ 6 ⇒ dp[i] = 6
對於後面的,有2種決策,搭樓梯 or 投骰子 之間取一個min就好
dp[i]=j=16min((dp[i+j]+1)/6,(dp[t[i+j]+1)/6])dp[i] = \sum_{j=1}^{6}min((dp[i + j] + 1)/6,(dp[t[i+j] +1)/6])

java代碼

import java.util.*;
import java.io.*;
 
public class Main{
	static double dp[];
	public static void main(String argv[]) throws IOException {
		StreamTokenizer in = new StreamTokenizer(
				new BufferedReader(new InputStreamReader(System.in)));
		PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
		int id[][] = new int[15][15];
		int t[] = new int[110];
		int num = 100;
		dp = new double[110];
		for(int i = 1;i <= 10;i++) {
			int tp,p;
			if(i%2==0) {
				tp = 10;p = -1;
			}else {
				tp = 1;p = 1;
			}
			for(int j = 1;j <= 10;j++) {
				id[i][tp] = num--;
				tp += p;
			}
		}
		/*for(int i = 1;i <= 10;i++) {
			for(int j = 1;j <= 10;j++) {
				out.printf("%d ", id[i][j]);
			}
			out.printf("\n");
		}*/
		Arrays.fill(t, 0);
		for(int i = 1;i <= 10;i++) {
			for(int j = 1;j <= 10;j++) {
				int x;in.nextToken();
				x = (int)in.nval;
				t[id[i][j]] = id[i-x][j];
				//out.printf("%d ",t[id[i][j]]);
			}
			//out.printf("\n");
		}
		dp[100] = 0.0;
		for(int i = 99;i >= 94;i--)dp[i] = 6.0;
		for(int i = 93;i >= 1;i--) {
			double ans = 0;
			for(int j = 1;j <= 6;j++) {
				ans += Math.min((dp[i+j]+1)/6.0,(1+dp[t[i+j]])/6.0);
			}
			dp[i] = ans;
		}
		out.printf("%.10f\n", dp[1]);
		out.close();
	}
}

F:
題意:一個區間[l,r],問你這個區間有多少對數滿足x + y = x ^ y ;
思路:很明顯每位取的情況只有3種,{0 ,1} {1 ,0 } {0 ,0 }
先轉化爲2進制
考慮dp[i][][]表示前i位數x,y是否達到上限的組合數;

import java.util.*;
import java.io.*;
 
public class Main{
	static final int man = 35+10;
	static long dp[][][];
	static int x[],y[];
	public static void main(String argv[]) throws IOException {
		StreamTokenizer in = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
		PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
		int t;
		in.nextToken();
		t = (int)in.nval;
		while(t-->0) {
			int l,r;
			in.nextToken();
			l = (int)in.nval;
			in.nextToken();
			r = (int)in.nval;
			long ans = slove(r,r) - slove(l-1,r) - slove(r,l-1) + slove(l-1,l-1);
			out.println(ans);
			out.flush();
		}
		out.close();
	}
	
	static long slove(int a,int b) {
		if(a<0||b<0)return 0;
		int pos = 0;
		dp = new long[50][2][2];
		x = new int[50];
		y = new int[50];
		for(int i = 0;i < 35;i++) {
			for(int j = 0;j < 2;j++) {
				for(int k = 0;k < 2;k++) {
					dp[i][j][k] = -1;
					//System.out.println(i + " " + j + " " + k);
				}
			}
		}
		while(a!=0||b!=0) {
			//System.out.println(pos + " " + a + " " + b);
			x[++pos] = a&1;
			y[pos] = b&1;
			a /= 2;
			b /= 2;
		}
		return dfs(pos,1,1);
	}
	
	static long dfs(int pos,int t1,int t2) {
		if(pos==0)return 1;
		if(dp[pos][t1][t2] != -1)return dp[pos][t1][t2];
		long ans =  0;
		int up1 = t1==1 ? x[pos] : 1;
		int up2 = t2==1 ? y[pos] : 1;
		for(int i = 0;i <= up1;i++) {
			for(int j  = 0;j <= up2;j++) {
				if(i==1&&j==1) continue;
				int a1 = 0,b1 = 0;
				if(t1==1&&i==up1)a1 = 1;
				if(t2==1&&j==up2)b1 = 1;
				ans += dfs(pos-1,a1,b1);
			}
		}
		dp[pos][t1][t2] = ans;
		return ans;
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章