简单模拟选做 & C++特性

海港

重点在于想到我们不关心每个人是第几批次,不关心同一时间同一国籍有几个人,所以可以只储存去重后的每个人的国籍和时间信息。

使用queue和map和pair,stl就是好用(

#include<iostream>
#include<cstdio>
#include<queue>
#include<map>

using namespace std;

const int N=200000;
int n,t[N];
queue<pair<int,int>>q,p;

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		int k;scanf("%d%d",&t[i],&k);
		map<int,int>m;
		for(int j=1;j<=k;++j){
			int x;scanf("%d",&x);
			if(m[x]) continue;
			m[x]=1;
			q.push(make_pair(t[i],x));
		}
	}
	map<int,int>m1;
	for(int i=1;i<=n;++i){
		while(p.front().first<=t[i]-86400&&!p.empty()){
			if(--m1[p.front().second]==0) m1.erase(p.front().second);
			p.pop();
		}
		while(q.front().first<=t[i]&&!q.empty()){
			++m1[q.front().second];
			p.push(q.front());q.pop();
		}
		printf("%d\n",m1.size());
	}
	return 0;
}

Home

题意:给定序列\(a_n\)和常数\(p\),求

\[\Sigma^{n}_{i=1}(2|x-a_i|+px) \]

的最小值

解法:三分法求单峰函数极值,略卡常

#include<iostream>
#include<cstdio>
#include<cmath>

#define ll long long
using namespace std;

const int N=1000010;
int n,p;
int a[N];

inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0' && ch<='9')
        x=x*10+ch-'0',ch=getchar();
    return x*f;
}

inline ll cal(ll x){
	ll ans=p*x;
	for(int i=1;i<=n;++i){
		ans+=abs(a[i]-x)*2;
	}
	return ans;
}

inline int tri(int l,int r){
	if(l==r) return l;
	if(r-l==1){
		ll cl=cal(l),cr=cal(r);
		if(cl<=cr) return l;
		return r;
	}
	if(r-l==2){
		ll c1=cal(l),c2=cal((l+r)/2),c3=cal(r);
		if(c1<=c2&&c1<=c3) return l;
		if(c2<=c3) return (l+r)/2;
		return r;
	}
	int m1=l+(r-l)/3,m2=r-(r-l)/3;
	ll c1=cal(m1),c2=cal(m2);
	if(c1>c2) return tri(m1,r);
	return tri(l,m2);
}

int main(){
	int T;T=read();while(T--){
//		scanf("%d%d",&n,&p);
		n=read();p=read();
		int l=N,r=-N;
		for(int i=1;i<=n;++i){
//			scanf("%d",&a[i]);
			a[i]=read();
			l=min(l,a[i]);r=max(r,a[i]);
		}
		if(abs(p)>n*2) {printf("No\n");continue;}
		if(abs(p)==n*2&&p>0) {printf("No\n");continue;}
		int ans=tri(l,r);
		printf("%d\n",ans);
	}
	return 0;
}

计算器的改良

注意一点细节:a+2a=4这个式子a默认系数是1;-2x=0解出来会x=-0.000,需要特判。。。

#define eps 0.000006
int main(){
	char n,x;int a=0,b=0,c=0,d=0,flag=0,tmp=0,minus=1;double ans;
	while(n=getchar()){
		if(n>='0'&&n<='9') tmp=tmp*10+n-'0';
		else{
			tmp*=minus;
			if(n>='a'&&n<='z'){
				x=n;
				if(tmp==0) tmp=1;
				if(flag) c+=tmp;
				else a+=tmp;
			}else{
				if(flag) d+=tmp;
				else b+=tmp;
			}tmp=0;minus=1;
			if(n=='=') flag=1;
			if(n=='-') minus=-1;
		}
		if(n=='\n') break;
	} ans=1.0*(d-b)/(a-c);
	if(fabs(ans)<eps) ans=0.0;
	printf("%c=%.3lf",x,ans);
	return 0;
}

跳房子

二分答案,需要维护RMQ,强制在线

维护RMQ有很多方法,最简单的ST表、单调队列、线段树,还有比较高级的FourRussian和笛卡尔树。这里ST表会爆空间(\(O(n^2)\)

因此使用deque实现单调队列维护RMQ,每次判断复杂度为线性,总复杂度\(O(nlogX)\)

不知道有没有什么更优雅的方法可以给f数组赋初始值,0x3f到底是什么原理?

感觉我的代码重构能力有待提升,不过最好还是 think twice code once 吧

这道题是NOIP2017普及组T4,也是我第一年参加普及组,没想到现在才补完题w

#include<bits/stdc++.h>
#define ll long long 
using namespace std;

const int inf=1000000000;
int n,d,k,maxx,b_;
int x[1000000],s[1000000];
ll f[1000000];
deque<int> q;

bool solve(int g){
	q.clear();memset(f,-0x3f,sizeof(f));f[0]=0;q.push_back(0);
	int now=1;
	for(int i=1;i<=n;++i){
		int l=max(0,x[i]-(d+g)),r=min(x[i]-1,x[i]-(d-g));
		if(r<0) continue;
		while(!q.empty()&&x[q.front()]<l) q.pop_front(); 
		while(x[now]<=r){
			while(x[now]<l) ++now;
			while(!q.empty()&&f[q.back()]<=f[now]) q.pop_back();
			q.push_back(now);++now;
		}
		if(!q.empty()) f[i]=f[q.front()]+s[i];
		if(f[i]>=k) return b_=1;
	}
	return 0;
}

int main(){
	cin>>n>>d>>k;
	for(int i=1;i<=n;++i) {cin>>x[i]>>s[i];maxx=max(maxx,x[i]);}
	int l=0,r=maxx;while(l<=r){
		int mid=(l+r)/2;
		if(solve(mid)) r=mid-1;
		else l=mid+1;
	}
	if(b_) cout<<l<<endl;
	else cout<<-1<<endl;
	return 0;
}

求和

这个求和公式 \((x+z)*(number_x+number_z)\) 果然是暗藏玄机

首先注意到三元组的分数与中间项无关,考虑分奇偶分别处理首尾项。把每种颜色单独提取出来,两两加和计算贡献,复杂度 \(O(n^2)\)

考虑优化:

\[ \sum x_i\sum num_i\newline =(n-1)\sum{x_inum_i}+\sum_{i\ne j}{x_i num_j}\newline =(n-2)\sum{x_inum_i}+\sum{x_i}\sum{num_i} \]

单独验证一下 n=1 和 n=2 符合,这样可以优化到\(O(nlogn)\) ,log是我用优先队列筛选颜色整出来的,也可以用空间换时间优化到 \(O(n)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N=200000,mod=10007;
int n,m,ans;
struct node{
	int x,num,clr;
	bool operator <(const node &temp)const{
		return clr<temp.clr;
	}
};
priority_queue<node>q0,q1;
int tmp1[N],tmp2[N];

signed main(){
	cin>>n>>m;
	for(int i=1;i<=n;++i) cin>>tmp1[i];
	for(int i=1;i<=n;++i) cin>>tmp2[i];
	for(int i=1;i<=n;++i){
		node tmp={i,tmp1[i],tmp2[i]};
		if(i%2) q1.push(tmp);
		else q0.push(tmp);
	}
	while(!q0.empty()){
		node now=q0.top();q0.pop();
		if(q0.empty()) break;
		int sum=now.x*now.num,sumx=now.x,sumn=now.num,cnt=1;
		while(q0.top().clr==now.clr){
			node t=q0.top();q0.pop();
			sum+=t.x*t.num;sumx+=t.x;sumn+=t.num;
			++cnt;
			if(q0.empty()) break;
		}
		ans=(ans+(cnt-2)*sum+sumx*sumn)%mod;
	}
	while(!q1.empty()){
		node now=q1.top();q1.pop();
		if(q1.empty()) break;
		int sum=now.x*now.num,sumx=now.x,sumn=now.num,cnt=1;
		while(q1.top().clr==now.clr){
			node t=q1.top();q1.pop();
			sum+=t.x*t.num;sumx+=t.x;sumn+=t.num;
			++cnt;
			if(q1.empty()) break;
		}
		ans=(ans+(cnt-2)*sum+sumx*sumn)%mod;
	}
	cout<<ans<<endl;
	return 0;
}

求交集

题目链接

#include<iostream>
#include<cstdio>
#include<vector>
#include<sstream>
#include<algorithm>
#include<set>
using namespace std;

int main(){
    string A, B, tmp;
    getline(cin, A);getline(cin, B);
    istringstream Astream(A), Bstream(B);
    vector<int> a, b, c;
    while(getline(Astream, tmp, ',')) a.push_back(stoi(tmp));
    while(getline(Bstream, tmp, ',')) b.push_back(stoi(tmp)); 
    for(int i = 0; i < a.size(); ++i){
        for(int j = 0; j < b.size(); ++j){
            if(a[i] == b[j]) c.push_back(a[i]);
        }
    }
    if(c.empty()) cout<<"NULL"<<endl;
    else{
        sort(c.begin(), c.end());
        cout<<c[0];
        for(int i = 1; i < c.size(); ++i)
            if(c[i]!=c[i-1])
                cout<<','<<c[i];
    }
}

根据日期求星期:

题目链接

ctime库

#include <iostream>
#include <ctime>
#include <cstring>
using namespace std;

const string week[8] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

bool f(string x, string y){
	int lenx = x.length(), leny = y.length();
	for(int i = 0; i + leny <= lenx; ++i){
		if(x.substr(i, leny) == y) return 1;
	}
	return 0;
}

int getMonth(string x){
	if(f(x, "Jan")) return 1;
	if(f(x, "Feb")) return 2;
	if(f(x, "Mar")) return 3;
	if(f(x, "Apr")) return 4;
	if(f(x, "May")) return 5;
	if(f(x, "Jun")) return 6;
	if(f(x, "Jul")) return 7;
	if(f(x, "Aug")) return 8;
	if(f(x, "Sep")) return 9;
	if(f(x, "Oct")) return 10;
	if(f(x, "Nov")) return 11;
	if(f(x, "Dec")) return 12;
	return -1;
}

int getYear(string x){
	int a[3], cnt = 0;
	for(int i = 0; i < x.length(); ++i)
		if(x[i] == '-') a[++cnt] = i;
	if(a[1] == 4) return stoi(x.substr(0, 4));
	if(a[2] == 6) return stoi(x.substr(7, 4));
	return stoi(x.substr(a[1]+1, 4));
}

int getDay(string x){
	int a[3], cnt = 0;
	for(int i = 0; i < x.length(); ++i)
		if(x[i] == '-') a[++cnt] = i;
	if(a[1] == 2) return stoi(x.substr(0, 2));
	if(a[2] == 8) return stoi(x.substr(9, 2));
	return stoi(x.substr(a[1]+1, 2));
}

int main() {
	int T;cin>>T;
	while(T--){
		string a;cin>>a;
		int mon = getMonth(a), 
		day = getDay(a), 
		year = getYear(a);
//		cout<<mon<<' '<<day<<' '<<year<<endl;

	    struct tm date = {};
	    date.tm_year = year - 1900;
	    date.tm_mon = mon - 1;
	    date.tm_mday = day;
		time_t t = mktime(&date);
    	tm* localTime = localtime(&t);
    	cout<<week[localTime->tm_wday]<<'.'<<endl;
	}
    return 0;
}

好人坏人

约瑟夫,但是不需要考虑具体的位置,只需要考虑是否位于前k个即可

原文链接

#include <iostream>
#include <cstdio>
using namespace std;
int solve(int k, int m) {
	int cur = 0, now = k * 2;
	while(now > k){
		cur = (cur - 1 + m) % now;
		if(cur < k) return 0;
		--now;
	}return 1;
}

int main() {
	int k; cin >> k;
	while (k) {
		for (int i = 2; ; ++i) {
			if (solve(k, i)) {
				cout << i << endl;
				break;
			}
		}
		cin >> k;
	}
	return 0;
}

[ZJOI2008]泡泡堂

双指针对田忌和齐威王的马进行比较,每次贪心选择看起来最好的方案。

#include <iostream>
#include <cstdio>
#include <algorithm>

using namespace std;

int n, a[200000], b[200000];

bool cmp(int x, int y){
	return x > y;
}

int f(int* a, int* b){
	int la = 1, ra = n, lb = 1, rb = n, ans = 0;
	for(int i = 1; i <= n; ++i){
		if(a[la] > b[lb]){
			++la;++lb;++ans;
		}else if(a[la] < b[lb]){
			--ra;++lb;--ans;
		}else{
			if(a[ra] < b[rb]){
				--ra;++lb;--ans;
			}else if(a[ra] > b[rb]){
				--ra;--rb;++ans;
			}else{
				if(a[ra] < b[lb]){
					--ans;
				}
				--ra;++lb;
			}
		}
	}
	return ans + n;
}

int main(){
//	while(1){
		cin>>n;
//		if(n == 0) break;
		for(int i = 1; i <= n; ++i) cin>>a[i]; //Tianji
		for(int i = 1; i <= n; ++i) cin>>b[i]; //Qiweiwang
		sort(a + 1, a + n + 1, cmp);sort(b + 1, b + n + 1, cmp);
		cout<<f(a, b)<<' '<<n * 2 - f(b, a)<<endl;
//	}
	return 0;
}

四则运算表达式的值

处理了左括号右边是正负号和等式开头是正负号的情况,并且把中缀转后缀和后缀表达式求值合并到一起进行。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>

using namespace std;

int judge(char ch){
	if(ch == '+' || ch == '-') return 1;
	if(ch == '*' || ch == '/') return 2;
	if(ch == '(' || ch == ')') return 0;
	if(ch >= '0' && ch <= '9') return -1;
	return -2;
}

int suf(string str){
	string ans;
	stack<int> nums;
	stack<char> opers;
	if(judge(str[0]) > 0) nums.push(0);
	for(int i = 0; i < str.length(); ++i){
		int typ = judge(str[i]);
		if(typ == -1){
			string tmp;tmp += str[i];
			while(judge(str[i + 1]) == -1) tmp += str[++i];
			nums.push(stoi(tmp));
		}else if(typ > 0){
			if(opers.empty() || judge(opers.top()) < typ){
				opers.push(str[i]);
			}else{
				while(!opers.empty() && judge(opers.top()) >= typ){
					char oper = opers.top();opers.pop();
					int y = nums.top();nums.pop();
					int x = nums.top();nums.pop();
					if(oper == '+') nums.push(x + y);
					if(oper == '-') nums.push(x - y);
					if(oper == '*') nums.push(x * y);
					if(oper == '/') nums.push(x / y);
				}
				opers.push(str[i]);
			}
		}else if(str[i] == '('){
			if(judge(str[i + 1]) > 0) nums.push(0);
			opers.push('(');
		}else if(str[i] == ')'){
			char oper = opers.top();opers.pop();
			while(oper != '('){
				int y = nums.top();nums.pop();
				int x = nums.top();nums.pop();
				if(oper == '+') nums.push(x + y);
				if(oper == '-') nums.push(x - y);
				if(oper == '*') nums.push(x * y);
				if(oper == '/') nums.push(x / y);
				oper = opers.top();opers.pop();
			}
		}
	}
	while(!opers.empty()){
		char oper = opers.top();opers.pop();
		int y = nums.top();nums.pop();
		int x = nums.top();nums.pop();
		if(oper == '+') nums.push(x + y);
		if(oper == '-') nums.push(x - y);
		if(oper == '*') nums.push(x * y);
		if(oper == '/') nums.push(x / y);
	}                              
	return nums.top();             
}

int main() {
	string str;
	while(cin>>str)
		cout<<suf(str)<<endl;
    return 0;
}

愉悦的假期(USACO11NOV)

分两种情况,一种是三个块两两连接,一种是一个点直接连三个块。

第一种情况\(n^4\)的枚举每两个点然后计算距离并统计,第二种情况对每个空点\(n^2\)的枚举X点,先标记后统计。总复杂度\(O(n^4)\)

#include <iostream>
#include <queue>
#include <utility>
#include <vector>
#include <map>
#include <cstring>

using namespace std;

//const int inf = 1e9;
const int N = 51;
int n, m, cnt = 0, maxx = 0;
char mp[N][N];
int fax[N][N], fay[N][N];
int ans[N][N][N][N];
int dx[4] = {0, 0, -1, 1};
int dy[4] = {-1, 1, 0, 0};

void dfs(int x, int y, int fx, int fy){
	fax[x][y] = fx;fay[x][y] = fy;
	for(int i = 0; i < 4; ++i){
		int nx = x + dx[i], ny = y + dy[i];
		if(mp[nx][ny] == 'X' && (fax[nx][ny] == 0 && fay[nx][ny] == 0))
			dfs(nx, ny, fx, fy);
	}
}

int main() {
	cin>>n>>m;
	for(int i = 1; i <= n; ++i) for(int j = 1; j <= m; ++j) cin>>mp[i][j];
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= m; ++j){
			if(mp[i][j] == '.') continue;
			if(fax[i][j] && fay[i][j]) continue;
			dfs(i, j, i, j);
		}
	for(int x1 = 1; x1 <= n; ++x1)
	for(int y1 = 1; y1 <= m; ++y1){
		if(mp[x1][y1] == '.') continue;
		for(int x2 = 1; x2 <= n; ++x2)
		for(int y2 = 1; y2 <= m; ++y2){
			if(mp[x2][y2] == '.') continue;
			if(fax[x1][y1] == fax[x2][y2] && fay[x1][y1] == fay[x2][y2]) continue;
			int fx1 = fax[x1][y1], fy1 = fay[x1][y1], fx2 = fax[x2][y2], fy2 = fay[x2][y2];
			if(ans[fx1][fy1][fx2][fy2] == 0) ans[fx1][fy1][fx2][fy2] = abs(x1-x2)+abs(y1-y2)-1;
			else ans[fx1][fy1][fx2][fy2] = ans[fx2][fy2][fx1][fy1] = min(ans[fx1][fy1][fx2][fy2], abs(x1-x2)+abs(y1-y2)-1);
		}
	}
	for(int x1 = 1; x1 <= n; ++x1)
	for(int y1 = 1; y1 <= m; ++y1)
	for(int x2 = 1; x2 <= n; ++x2)
	for(int y2 = 1; y2 <= m; ++y2)
		if(ans[x1][y1][x2][y2]){
			if(x2 > x1 || (x2 == x1 && y2 > y1)) continue;
			cnt += ans[x1][y1][x2][y2];
			maxx = max(maxx, ans[x1][y1][x2][y2]);
		}
	cnt -= maxx;
	int num = cnt;
	map<pair<int, int>, int> man;
	for(int i = 1; i <= n; ++i)
	for(int j = 1; j <= m; ++j){
		if(mp[i][j] == 'X') continue;
		man.clear();
		for(int x = 1; x <= n; ++x)
		for(int y = 1; y <= m; ++y){
			if(mp[x][y] == '.') continue;
			if(!man[make_pair(fax[x][y], fay[x][y])]) man[make_pair(fax[x][y], fay[x][y])] = abs(i-x)+abs(j-y);
			else man[make_pair(fax[x][y], fay[x][y])] = min(man[make_pair(fax[x][y], fay[x][y])], abs(i-x)+abs(j-y));
		}
		int tmp = 0;
		for(auto m : man) tmp += m.second;
		num = min(num, tmp-2);
//		cout<<man.size()<<' '<<m<<endl;
	}
	cout<<num<<endl;
	return 0;
}

为运算表达式添加括号

题目来源:北大24fall计概A期末考试,题目链接

思路:区间dp,每个dp状态为一个map,记录每个答案出现的次数。

难点:

  1. 必须使用dp或者记忆化搜索,不然重复计算会导致无法通过。
  2. dp状态的vector可能有极多的重复,导致每次状态转移的代价极大。所以使用map效率更高。
#include <iostream>
#include <cstdio>
#include <vector>
#include <map>
#include <string>

using namespace std;

vector<int> nums;
vector<char> chars;
map<pair<int, int>, map<int, int>> dp;

pair<int, int> mp(int lft, int rht) {
    return make_pair(lft, rht);
}

bool isChar(char ch) {
    return ch == '+' || ch == '-' || ch == '*';
}

void init(string s) {
    int num = 0;
    int len = s.length();
    for (int i = 0; i < len; ++i) {
        if (isChar(s[i])) {
            nums.push_back(num);
            chars.push_back(s[i]);
            num = 0;
        } else {
            num = num * 10 + s[i] - '0';
        }
    }
    nums.push_back(num);
}

void cal(int l1, int r1, int l2, int r2, char ch) {
    if (ch == '+') {
        for (auto i : dp[mp(l1, r1)]) {
            for (auto j : dp[mp(l2, r2)]) {
                dp[mp(l1, r2)][i.first + j.first] += i.second * j.second;
            }
        }
    } else if (ch == '-') {
        for (auto i : dp[mp(l1, r1)]) {
            for (auto j : dp[mp(l2, r2)]) {
                dp[mp(l1, r2)][i.first - j.first] += i.second * j.second;
            }
        }
    } else if (ch == '*') {
        for (auto i : dp[mp(l1, r1)]) {
            for (auto j : dp[mp(l2, r2)]) {
                dp[mp(l1, r2)][i.first * j.first] += i.second * j.second;
            }
        }
    }
}

void DP() {
    int n = nums.size();
    for (int i = 0; i < n; ++i) {
        ++dp[mp(i, i)][nums[i]];
    }
    for (int len = 1; len < n; ++len) {
        for (int l = 0; l + len < n; ++l) {
            int r = l + len;
            for (int i = l; i < r; ++i) {
                cal(l, i, i + 1, r, chars[i]);
            }
        }
    }
    // for (auto i : dp[mp(0, n - 1)]) {
    //     cout << i.first << ' ' << i.second << endl;
    // }
    int sum = 0;
    for (auto i : dp[mp(0, n - 1)]) {
        for (int j = i.second; j > 0; --j) {
            cout << i.first << ' ';
            ++sum;
            if (sum == 1000) {
                return;
            }
        }
    }
}

signed main() {
    string input;
    cin >> input;
    init(input);
    DP();
    return 0;
}
posted @ 2024-03-22 18:26  hcx1999  阅读(32)  评论(0)    收藏  举报