第五届辽宁省大学生程序设计竞赛(大连)
Prepared by Beater , Core_65536
A.爱上字典
思路:
不断读入字符串,将大写字母全转换成小写字母,特殊字符忽略,将每个字符串存入map中,读到数字就退出。
读入已经会的n个单词,如果这个单词在map中则将这个单词从map中删去,答案就是map中剩余的字符串个数
代码:
#include
using namespace std;
typedef long long ll;
#define endl '\n'
void solve()
{
map
string s;
while(1)
{
cin>>s;
//mp[s]++;
int f=0;
for(auto i:s)
if(isdigit(i))
{
f=1;
break;
}
if(f) break;
string t;
for(auto i:s)
if(isupper(i)) t+=tolower(i);
else if(islower(i)) t+=i;
mp[t]++;
}
int t=0;
for(auto i:s) t=t*10+(i-'0');
while(t--)
{
string s;
cin>>s;
if(mp.count(s)) mp.erase(s);
}
cout< } int main (){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int t=1; // cin>>t; while(t--){ solve(); } return 0; } B.比分幻术 思路: 读入字符串,交换一下两个比分,输出 题目还没看,队友样例都没测就交了( 代码: #include using namespace std; typedef long long ll; #define endl '\n' void solve(){ string s; cin>>s; swap(s[0],s[2]); cout< } int main (){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int t=1; // cin>>t; while(t--){ solve(); } return 0; } C.插排串联 思路: dfs求出每个插座需要承载的功率,将所有插座的承载功率排个序,再将每个插排的限制功率排个序,按顺序依次匹配,若无法承载功率则输出"NO"(包括根节点)否则输出"YES" 代码: #include using namespace std; typedef long long ll; #define endl '\n' #define int long long vector int n,f[100010],a[100010]; int sum[100010]; vector void dfs(int u,int fa) { if(g[u].size()==1&&u!=0) { vt.push_back(u); sum[u]=a[u]; return ; } sum[u]=0; for(auto v:g[u]) { if(v==fa) continue; dfs(v,u); sum[u]+=sum[v]; } } int vis[100010]; void solve() { cin>>n; for(int i=1;i<=n;i++) { vis[i]=0; cin>>f[i]>>a[i]; g[f[i]].push_back(i); g[i].push_back(f[i]); } dfs(0,-1); for(auto i:vt) vis[i]=1; vector vector for(int i=1;i<=n;i++) if(!vis[i]) rs.push_back(sum[i]); for(int i=1;i<=n;i++) if(!vis[i]) ra.push_back(a[i]); sort(rs.begin(),rs.end()); sort(ra.begin(),ra.end()); for(int i=0;i if(rs[i]>ra[i]) { cout<<"NO\n"; return ; } int s=0; for(auto i:g[0]) { s+=sum[i]; } if(s>2200) { cout<<"NO\n"; return ; } cout<<"YES\n"; } signed main () { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int t=1; // cin>>t; while(t--){ solve(); } return 0; } D.都市叠高 思路: 诈骗题,看着像计算几何实际就是一个dp 每一次叠楼层增长的高度一定是凸包中某两个点的线段距离,考虑到n为5000,直接暴力\(n^2\)求出所有点两两之间的距离,当选择两个点\((j,i)\)后,这两个点之间的所有点都将失效,因此状态应该从第一个点j前面转移而来,状态转移方程: $ dp_i =dp_{j-1} + dis(j,i) $ 时间复杂度\(O(n^2)\) 代码: #include using namespace std; typedef long long ll; #define endl '\n' ll n,x[6010],y[6010]; long double dis(int i,int j) { return sqrtl(powl(x[i]-x[j],2.0)+powl(y[i]-y[j],2.0)); } long double dp[6010]; void solve() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%lld %lld",&x[i],&y[i]); dp[0]=0; for(int i=1;i<=n;i++) { dp[i]=dp[i-1]; for(int j=1;j
{ long double d=dis(i,j); dp[i]=max(dp[i],dp[j-1]+d); } } long double ans1=0; for(int i=1;i<=n;i++) ans1=max(ans1,dp[i]); printf("%.20llf\n",ans1); } int main () { int t=1; // cin>>t; while(t--){ solve(); } return 0; } E.俄式简餐 思路: 若n和m有一边可以被4整除,则用俄罗斯方块2填满 1 1 1 1 若n和m有一边减去6后能被4整除,另一条边能被2整除 则这样填满 1 1 1 2 2 2 4 4 4 4 1 3 3 3 3 2 5 5 5 5 首先由题得,当\(nm/4\)不整除时必定无解。 然后发现当n或m整除4时可以轻松构造。 所以此时还剩下:nm整除4 且 nm都不为4倍数 的情况。 于是我们知道,这种情况即n、m至少有一个是2的倍数。 于是从最小的开始考虑,便构造出了\(6*2\)的情况,接着发现可以递推得出\((6+4k)*2\)的构造方式。 又考虑到\(n*2\)的构造方式只对\((6+4k)*2\)或\(4*2\)有解。 所以我们知道,以上即为所有情况。 代码: #include using namespace std; typedef long long ll; #define endl '\n' void solve(){ int n,m; cin>>n>>m; vector if(m%4==0){ int cnt=1; for(int i=1;i<=n;i++) { int count=0; for(int j=1;j<=m;j++) { ans[i][j]=cnt; count++; if(count==4){ count=0; cnt++; } } } }else if(n%4==0){ int cnt=1; for(int j=1;j<=m;j++){ int count=0; for(int i=1;i<=n;i++){ ans[i][j]=cnt; count++; if(count==4){ count=0; cnt++; } } } }else if(m>=6&&(m-6)%4==0&&n%2==0){ int cnt=1; auto sd=[&](int x,int y){ for(int j=y;j ans[x+1][j]=cnt; } ans[x][y]=cnt; cnt++; for(int j=y+3;j ans[x+1][j]=cnt; } ans[x][y+5]=cnt; cnt++; for(int j=y+1;j ans[x][j]=cnt; } cnt++; }; for(int i=1;i<=n;i+=2){ sd(i,1); } for(int i=1;i<=n;i++){ int count=0; for(int j=7;j<=m;j++){ ans[i][j]=cnt; count++; if(count==4){ count=0; cnt++; } } } }else if(n>=6&&(n-6)%4==0&&m%2==0){ int cnt=1; auto sd1=[&](int x,int y){ for(int i=x;i ans[i][y]=cnt; } ans[x][y+1]=cnt; cnt++; for(int i=x+3;i ans[i][y]=cnt; } ans[x+5][y+1]=cnt; cnt++; for(int i=x+1;i ans[i][y+1]=cnt; } cnt++; }; for(int j=1;j<=m;j+=2){ sd1(1,j); } for(int j=1;j<=m;j++){ int count=0; for(int i=7;i<=n;i++){ ans[i][j]=cnt; count++; if(count==4){ count=0; cnt++; } } } }else{ cout<<"NO"< return ; } cout<<"YES"< for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ cout< } cout< } } int main (){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int t=1; cin>>t; while(t--){ solve(); } return 0; } G.顾影自怜 思路: 二分加st表。 对于一个元素x,找出右边界使得这个区间内最大值为x,找出元素x出现k次时的下标,这个区间就是贡献。例如:3 3 2 3 1 2 4,当k为3时,对于第一个元素3下标为1,第k个3下标为4,右边界为6,那么这一部分贡献就是6-4+1=3(这个只是单纯的加1),即(3 3 2 3)、(3 3 2 3 1)、(3 3 2 3 1 2)都可以为答案。接下来统计左边的贡献,向左找左边界,使该元素左边的元素都比他小,这样不会造成重复统计,例如3 1 2 3 3 2 3 1 2 4,(3 3 2 3)、(2 3 3 2 3)、(1 2 3 3 2 3)都可以为答案。我们将左右两边的贡献相乘就是这个下标的元素的总贡献,将所有元素的贡献相加即可 时间复杂度\(O(nlog)\) 代码: #include using namespace std; typedef long long ll; #define endl '\n' typedef unsigned long long ull; const int MAXN=2e6+7; int Log2[MAXN],Min[MAXN][21],Max[MAXN][21]; void solve(){ int n,k; cin>>n>>k; int a[n+1]; for(int i=1;i<=n;i++){ cin>>a[i]; } for(int i=1;i<=n;++i){ Min[i][0]=a[i]; Max[i][0]=a[i]; } for(int i=2;i<=n;++i){ Log2[i]=Log2[i/2]+1; } for(int i=1;i<=20;++i){ for(int j=1;j+(1<
Min[j][i]=min(Min[j][i-1],Min[j+(1<<(i-1))][i-1]); Max[j][i]=max(Max[j][i-1],Max[j+(1<<(i-1))][i-1]); } } vector vector for(int i=1;i<=n;++i){ idx[a[i]].push_back(i); vis[i]=idx[a[i]].size()-1; } auto check=[&](int l,int r){ int s=Log2[r-l+1]; int ma=max(Max[l][s],Max[r-(1< if(ma<=a[l]){ return 1; }else{ return 0; } }; auto check1=[&](int l,int r){ int s=Log2[r-l+1]; int ma=max(Max[l][s],Max[r-(1<