P1067 [NOIP 2009 普及组] 多项式输出

记录62

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n,t;
	bool f=0;
	cin>>n;
	for(int i=n;i>=0;i--){
		cin>>t;
		if(t==0) continue;
		if(t>0&&f) cout<<'+';
		if(abs(t)!=1||i==0) cout<<t;
		if(t==-1&&i!=0) cout<<'-';
		if(i>0) cout<<'x';
		if(i>=2) cout<<'^'<<i; 
		if(t!=0&&!f) f=1;
	}	 
	return 0;
} 

题目传送门https://www.luogu.com.cn/problem/P1067


突破口

一元 n 次多项式可用如下的表达式表示:

f(x)=。。。

其中,ai​xi 称为 i 次项,ai​ 称为 i 次项的系数。给出一个一元多项式各项的次数和系数,请按照如下规定的格式要求输出该多项式


思路

模拟一个一元 n 次多项式,先搞定系数,再搞定x的次方


代码简析

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n,t;
	bool f=0;
	cin>>n;
	for(int i=n;i>=0;i--){
		cin>>t;
		if(t==0) continue;
		if(t>0&&f) cout<<'+';
		if(abs(t)!=1||i==0) cout<<t;
		if(t==-1&&i!=0) cout<<'-';
		if(i>0) cout<<'x';
		if(i>=2) cout<<'^'<<i; 
		if(t!=0&&!f) f=1;
	}	 
	return 0;
} 

int n(一元多项式的最高次数),t(输入的每个数字);

bool f=0; (对第一个数字特殊处理,如果是正数不用输出+号)

for(int i=n;i>=0;i--){}  👉  把每一个项按照次数递减顺序输出

if(t==0) continue;  👉 系数为0直接跳过这一项

if(t>0&&f) cout<<'+';  👉  数字是整数,且是不是第一个数字,有+号

if(abs(t)!=1||i==0) cout<<t;  👉  系数不是1,或者是第最后一项,直接输出

注意:负数无论在第一项还是第n项,都需要带-符号

if(t==-1&&i!=0) cout<<'-';  👉  系数为-1时,并且不是最后一项,系数1省略,只有-号

if(i>0) cout<<'x';  👉 只要不是最后一项,都有x

if(i>=2) cout<<'^'<<i;   👉  除了最后两项项的x的次方要省略,其他的都要有次方

if(t!=0&&!f) f=1;  👉 保证第一项后的所有整数都带+号


补充

CSP-J字符串操作技巧完全汇总


一、输入输出处理(竞赛第一步)

1.1 不同读取方式

// 方式1:读取单词(遇空格/换行停止)
string s;
cin >> s;  // 输入"hello world",s="hello"

// 方式2:读取整行(含空格)
string line;
getline(cin, line);  // 输入"hello world",line="hello world"
// ⚠️ 注意:若前面用过cin,需先cin.ignore()清空缓冲区

// 方式3:读取到字符数组(C风格)
char str[1005];
scanf("%s", str);  // 读取单词
gets(str);         // ⚠️ 危险!读取整行,易溢出,禁用
fgets(str, 1005, stdin);  // 安全读取整行,但保留'\n'

1.2 处理空格与换行

// 需求:读取含空格的字符串
// 错误:cin >> s会截断
// 正确:
getline(cin, line);

// 读取多个数字字符串
while (cin >> s) {  // 自动忽略空格和换行
    // 处理s
}

二、基本遍历与修改(高频操作)

2.1 三种遍历方式

string s = "abcdef";

// 方式1:下标遍历(最通用)
for (int i = 0; i < s.size(); i++) {
    s[i] = toupper(s[i]);  // 转大写
}

// 方式2:范围for(简洁)
for (char &c : s) {  // &引用可修改
    if (c == 'a') c = 'b';
}

// 方式3:迭代器(少用)
for (auto it = s.begin(); it != s.end(); ++it) {
    *it = 'x';
}

2.2 修改字符

// 单点修改
s[0] = 'A';  // O(1)

// 尾部增删
s.push_back('x');  // 尾部加,O(1)
s.pop_back();      // 尾部删,O(1)

// 中间插入(性能差,避免)
s.insert(3, "abc");  // O(n),慎用

// 删除区间
s.erase(3, 3);  // 从位置3删3个字符,O(n),慎用

竞赛铁律中间插入/删除少用,用push_back/pop_back+重构


三、查找与匹配(CSP-J核心)

3.1 子串查找

string s = "hello world hello";

size_t pos = s.find("world");  // 返回首次位置,找不到返回npos
if (pos != string::npos) {
    // 找到
    cout << pos;  // 输出6
}

// 查找所有出现
size_t start = 0;
while ((pos = s.find("hello", start)) != string::npos) {
    cout << pos << " ";
    start = pos + 1;  // 从下一个位置继续找
}

3.2 字符查找

// 找字符首次出现
pos = s.find('o');  // O(n)

// 反向查找
pos = s.rfind('o'); // 从后往前找

陷阱返回size_t类型,不是int,比较时用string::npos(值为-1)


四、截取与拼接(高频)

4.1 截取子串

string s = "hello world";

string sub1 = s.substr(6, 5);  // 从位置6截取5个字符,"world"
string sub2 = s.substr(6);     // 从位置6到末尾,"world"

// 应用场景:分离路径
string path = "/home/user/file.txt";
size_t pos = path.rfind('/');  // 从后找'/'
string dir = path.substr(0, pos);  // "/home/user"
string file = path.substr(pos + 1); // "file.txt"

4.2 拼接字符串

// 方式1:+=(推荐)
string s = "a";
s += "b";        // s="ab"
s += 'c';        // s="abc"

// 方式2:append()
s.append("def"); // s="abcdef"

// 方式3:+(创建临时对象,性能稍差)
string s2 = s + "xyz";  // s不变,s2="abcxyz"

// 方式4:stringstream(复杂拼接)
ostringstream oss;
oss << "ID:" << id << ", Score:" << score;
string result = oss.str();

竞赛铁律:**循环内用+=,不要用+ **(避免创建临时对象)


五、类型转换(竞赛必背)

** 5.1 数字 → 字符串 **

// 方式1:to_string()(C++11,推荐)
int x = 123;
string s = to_string(x);  // "123"
ll y = 1234567890123LL;
string s2 = to_string(y); // "1234567890123"

// 方式2:sprintf()(C风格)
char buf[20];
sprintf(buf, "%d", x);
string s = buf;

// 方式3:stringstream(兼容C++98)
ostringstream oss;
oss << x;
string s = oss.str();

5.2 字符串 → 数字

// 方式1:stoi/stoll()(C++11,但Dev-C++可能不支持)
string s = "123";
int x = stoi(s);      // 转int
ll y = stoll(s);      // 转long long

// 方式2:atoi/atoll()(C风格,需c_str())
int x = atoi(s.c_str());    // 转int
ll y = atoll(s.c_str());    // 转long long

// 方式3:stringstream(最安全)
stringstream ss(s);
int x;
ss >> x;  // 成功返回1,失败返回0

// 方式4:手动实现(竞赛推荐)
int strToInt(string s) {
    int ans = 0, sign = 1, i = 0;
    if (s[0] == '-') { sign = -1; i = 1; }
    for (; i < (int)s.size(); i++) ans = ans * 10 + (s[i] - '0');
    return ans * sign;
}

竞赛铁律比赛中禁用stoi(),用atoi()或手写,避免编译失败


六、高级技巧(CSP-J压轴题)

6.1 前缀/后缀处理

// 前缀和
ll pre[N];
for (int i = 0; i < n; i++) pre[i+1] = pre[i] + (s[i] - '0');

// 前缀哈希(滚动哈希)
unsigned long long h = 0, base = 131;
for (char c : s) {
    h = h * base + c;
}

// 前缀函数(KMP的next数组)
int nxt[N];
for (int i = 1, j = 0; i < n; i++) {
    while (j > 0 && s[i] != s[j]) j = nxt[j-1];
    if (s[i] == s[j]) j++;
    nxt[i] = j;
}

6.2 双指针技巧

// 场景:找最长无重复子串
int cnt[256] = {0}, left = 0, ans = 0;
for (int right = 0; right < n; right++) {
    cnt[s[right]]++;
    while (cnt[s[right]] > 1) {  // 重复移动左指针
        cnt[s[left]]--;
        left++;
    }
    ans = max(ans, right - left + 1);
}

6.3 滑动窗口

// 场景:找包含所有模式串的最短子串
unordered_map<char, int> need, window;
int left = 0, right = 0, valid = 0;
while (right < n) {
    char c = s[right];
    window[c]++;
    if (need[c] == window[c]) valid++;
    while (valid == need.size()) {
        // 找到窗口,更新答案
        if (right - left + 1 < minLen) {
            minLen = right - left + 1;
            start = left;
        }
        char d = s[left];
        if (need[d] == window[d]) valid--;
        window[d]--;
        left++;
    }
    right++;
}

6.4 字符串反转

// 方式1:reverse算法
reverse(s.begin(), s.end());  // O(n)

// 方式2:手动交换
for (int i = 0, j = s.size() - 1; i < j; i++, j--) {
    swap(s[i], s[j]);
}

七、性能优化(防TLE)

7.1 避免低效操作

// ❌ 极低效:循环内频繁字符串拼接
string ans;
for (int i = 0; i < 1e6; i++) {
    ans += char('a' + i % 26);  // O(n²),1e12次操作
}

// ✅ 高效:预先reserve空间
ans.reserve(1e6);  // 预先分配内存
for (int i = 0; i < 1e6; i++) {
    ans.push_back('a' + i % 26);  // O(n)
}

// ✅ 更高效:用char[]再转string
char buf[1000005];
int len = 0;
for (int i = 0; i < 1e6; i++) {
    buf[len++] = 'a' + i % 26;
}
string ans(buf, len);

7.2 减少字符串拷贝

// ❌ 低效:传值
void func(string s) { ... }  // 拷贝一次

// ✅ 高效:传引用
void func(const string& s) { ... }  // 零拷贝

八、竞赛实战模板(背下来)

模板1:处理多行字符串

int n;
cin >> n;
cin.ignore();  // 跳过换行
for (int i = 0; i < n; i++) {
    string line;
    getline(cin, line);
    // 处理line
}

模板2:字符串分割

vector<string> split(const string& s, char delim) {
    vector<string> res;
    stringstream ss(s);
    string item;
    while (getline(ss, item, delim)) {
        res.push_back(item);
    }
    return res;
}
// 使用:vector<string> parts = split("a,b,c", ',');

模板3:快速字符计数

int cnt[256] = {0};
for (char c : s) cnt[c]++;
// 找最多字符
char maxChar = 0;
for (int i = 0; i < 256; i++) {
    if (cnt[i] > cnt[maxChar]) maxChar = i;
}

九、一句话总结

CSP-J字符串=输入用cin+getline,遍历用范围for,查找用find(),转换用to_string()和手写stoll,高级用双指针+前缀和,中间插入删除要避免,比赛混用printf记得c_str()。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值