A.
水题,set查重从小到大扫描即可。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<set>
using namespace std;
int main()
{
int t,i,j,k,n,num[200005];
cin>>t;
while(t--){
cin>>n;
set<int>s;
for(i=0;i<n;i++) {
scanf("%d", &j);
s.insert(j);
}j=0;
set<int>::iterator iter=s.begin();
for(;iter!=s.end();iter++){
if(*iter!=j){
cout<<j<<endl;break;
}
j++;
}
}
return 0;
}
B.
C.
大意就是给定一个字符串,求字符串中有多少个“bupt”子串,注意子串不需要连续,类似于最长公共子串的定义。
做法也有点类似于最长公共子串,按串中每一位进行dp。小心即可
#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <iostream>
using namespace std;
char a[1000666], b[5] = "bupt";
int f[1000666][6];//f[i][j]表示目标串的前i-1位与bupt的前j-1位匹配的可能数
int p = 1e9 + 7;
int main(){
int t;
cin>>t;
while (t--){
scanf("%s", a);
//memset(f, 0, sizeof(f));直接memset会tle
int n = strlen(a), m = 4;
for (int i = 0; i <= n; i++)
f[i][0] = 1;//只把有必要的部分进行memset即可
for (int i = 1; i <= n; i++)
for (int j = 1; j <= 4; j++){
f[i][j] = f[i-1][j] % p;//每一位显然首先等于上一位的可能值
if (a[i-1] == b[j-1])
f[i][j] = (f[i][j] + f[i-1][j-1]) % p;//如果这一位相等,那么就加
//cout<<i<<' '<<j<<' '<<f[i][j]<<endl;
}
printf("%d\n", f[n][m]);
}
}
更神奇的做法则不用dp,直接从前往后扫,类似于乘法原理,但是这里换成了加,大概就是每扫到一个需要的字母,就加上需要的字母前一位的字母的个数。具体可以体会一下:
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const long long P=1e9+7;
int T,len;
char s[1000100];
long long k1,k2,k3,ans;
int main()
{
cin>>T;
getchar();
while (T--)
{
len=0;
k1=k2=k3=ans=0;
for (char ch=getchar();ch!='\n';ch=getchar()) s[++len]=ch;
for (int i=1;i<=len;++i)
{
if (s[i]=='b') k1=(k1+1)%P;
if (s[i]=='u') k2=(k2+k1)%P;//相当于当前的u前面对应的b的个数
//决定了当前位置的u有多少种bu的可能
if (s[i]=='p') k3=(k3+k2)%P;
if (s[i]=='t') ans=(ans+k3)%P;
}
cout<<ans<<endl;
}
return 0;
}
D.
没有截图。题目大意是有很多堆石子,然后每一堆里面有若干石子,两个人玩游戏,每次可以选中一堆取石子,每次至少取一个石子,最多可以把整堆取走,每轮会给定一个游戏区间,也就是这轮游戏在第l~r堆石子上进行(这轮结束以后石子归位),每轮都是甲先手,然后甲赢的话f就是1,否则为0.
有多组test,对于每一组test,输出 fi*2^(m-i) 之和,m是每个test的游戏轮数,n为石子个数。
这是个经典的nim游戏,但是由于每轮游戏要进行n轮,所以为了高效,可以考虑弄个类似于前缀和那样的前缀xor数组,然后因为同一个数xor两次相当于抵消,所以可以高效求出。后面的幂方则可以搞一个快速幂。
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e6+5;
const int mod=1e9+7;
typedef long long ll;
ll num[N],xorr[N];
ll mod_pow(ll x, ll n, ll mod)
{
ll res = 1;
while (n) {
if (n & 1)
res = res*x%mod;
x = x*x%mod;
n >>= 1;
}
return res;
}
int main()
{
int t,i,j,n,m;
cin>>t;
while(t--){
ll ans=0;
cin>>n>>m;
for(i=1;i<=n;i++){scanf("%d",&num[i]);}
xorr[1]=num[1];//特判1的情况
for(i=2;i<=n;i++)xorr[i]=num[i]^xorr[i-1];
for(i=1;i<=m;i++){
int a,b;
scanf("%d%d",&a,&b);
if(a>1)
j=xorr[b]^xorr[a-1];//抵消前缀亦或
else
j=xorr[b];//l为1不用抵消
int f;
if(j){
f=1;
}
else f=0;
ans=(ans+mod_pow(2,m-i,mod)*f)%mod;
}
cout<<ans<<endl;
}
return 0;
}
E.
F.
大意就是给定一棵树,给定所有节点之间的距离(相邻的节点),每个节点都有自己的权值,然后求一个点使得 (所有其他点的权值*到这个点的距离) 的总和最小。注意,没有说明是二叉树。
题目做法有点类似于POJ3585,首先进行一次扫描,然后换根不停地转移,然后记录整个过程中的最小值并输出即可。
在第一次扫描的时候任选一个节点为根(比如1),然后扫描过程中处理处每个点的子节点的权值之和与(子节点的权值*路径长度)之和,保存在节点里,二次扫描换根的时候就会简单很多。
二次扫描的时候,则用预处理的值高效计算转移结果。
扫描换根如图所示
代码:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <iostream>
#include <vector>
using namespace std;
int n;
typedef long long ll;
struct data{
int y;
ll w;
};
vector<data> g[500055];
ll sum[500055];//每个节点的子节点的(权值*路径)和
ll cnt[500055];//每个节点的子节点权值和(包括节点本身的权值)
ll w[500055];//记录每个点的权值
//ll ans[500055];
ll emmm;//用于存储答案
void dfs(int x, int fa){//第一次扫描
cnt[x] = w[x];
sum[x] = 0;//cout<<x<<' '<<cnt[x]<<endl;
for (int i = 0; i < g[x].size(); i++){
int y = g[x][i].y;
ll W = g[x][i].w;
if (y == fa)
continue;
dfs(y, x);
cnt[x] += cnt[y];
sum[x] += cnt[y] * W + sum[y];
}
}
void dfs1(int x, int fa){//换根扫描
for (int i = 0; i < g[x].size(); i++){
int y = g[x][i].y;
ll W = g[x][i].w;
if (y == fa)
continue;
sum[y] = sum [y] + sum[x] - sum[y] - cnt[y] * W + (cnt[x] - cnt[y]) * W;
cnt[y] += cnt[x] - cnt[y];
emmm = min(emmm, sum[y]);
//cout<<sum[y]<<endl;
dfs1(y, x);
}
}
int main(){
//cout<<ans;
cin>>n;
for (int i = 1; i < n; i++){
int x, y; ll w;
scanf("%d%d%lld", &x, &y, &w);
g[x].push_back((data){y, w});
g[y].push_back((data){x, w});
}
for (int i = 1; i <= n; i++)
scanf("%lld", &w[i]);
dfs(1, -1);
emmm = sum[1];
//cout<<emmm<<endl;
dfs1(1, 0);
cout<<emmm;
return 0;
}
G.
大意就是给定一个坐标轴,轴上有若干个点,每个点有权值,求坐标轴上的一个点使得其他所有点的(权值*到这个点的距离)总和最小。
这其实是一个带权中位数的问题,三分也能过比赛数据…
带权中位数:https://blog.csdn.net/Clove_unique/article/details/50618784
代码:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
typedef unsigned long long ll;
struct data{
int x;//坐标
ll q;//权值
}a[100011];
bool cmp(data A, data B){
return A.x < B.x;
}
ll f(ll x, int t){
if (t == 1)
return x;
if (t == 2)
return x * x;
if (t == 3)
return x * x * x;
return x * x * x * x;
}
int main(){
int T;
cin>>T;
while (T--){
int n, t;
cin>>n>>t;
ll sum = 0;
for (int i = 1; i <= n; i++){
scanf("%lld%lld", &a[i].q, &a[i].x);
a[i].q = f(a[i].q, t);//预处理权值
sum += a[i].q;
}
sort(a + 1, a + 1 + n, cmp);
ll tmp = 0;
int i;
for (i = 1; i <= n; i++){
tmp += a[i].q;
if (tmp * 2 > sum)
break;
}
ll ans = 0;
for (int j = 1; j <= n; j++)
ans += abs(a[j].x - a[i].x) * a[j].q;
cout<<ans<<'\n';
}
return 0;
}
三分做法:
#include <bits/stdc++.h>
using namespace std;
const long long INF=9223372036854775807ll;
const int N=100005;
int n,t;
int q[N],x[N];
inline int read()
{
int a=0,f=1; char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
return a*f;
}
int pow(int x,int y)
{
int ans=1;
for (int i=1;i<=y;i++)
ans*=x;
return ans;
}
long long calc(int mid)
{
long long ans=0;
for (int i=1;i<=n;i++)
ans+=(long long)abs(mid-x[i])*q[i];
return ans;
}
int main()
{
int testcase=read();
while (testcase--)
{
n=read(); t=read();
int L=1000000,R=0;
for (int i=1;i<=n;i++)
q[i]=pow(read(),t),x[i]=read(),L=min(L,x[i]),R=max(R,x[i]);
while (R-L>5)
{
int lmid=(L+L+R)/3;
int rmid=(L+R+R)/3;
if (calc(lmid)<calc(rmid)) R=rmid;
else L=lmid;
}
long long ans=INF;
for (int i=L;i<=R;i++)
ans=min(ans,calc(i));
cout << ans << endl;
}
return 0;
}
H.
超级暴力的bfs爆搜:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <iostream>
#include <istream>
#define forr(a,b,c) for(int a=b;a<=c;a++)
#define MAX(a,b) (a>b)?a:b;
#define MIN(a,b) (a<b)?a:b;
#define memsett(a) memset(a,0,sizeof(a));
using namespace std;
char mapp[2002][2002];
bool ismap[2002][2002];
int loca[2][10000][2];
int a,b,final,finded;
bool boo;
int find(int x,int y){
if (ismap[x][y]==true) return 0;
else if (mapp[x][y]=='.'||mapp[x][y]=='T') return x*10000+y;
else if (mapp[x][y]=='U'){
ismap[x][y]=true;
x=x-1;
while(mapp[x][y]=='.')x=x-1;
if (mapp[x][y]=='#')x=x+1;
return find(x,y);
}
else if (mapp[x][y]=='D'){
ismap[x][y]=true;
x=x+1;
while(mapp[x][y]=='.')x=x+1;
if (mapp[x][y]=='#')x=x-1;
return find(x,y);
}
else if (mapp[x][y]=='L'){
ismap[x][y]=true;
y=y-1;
while(mapp[x][y]=='.')y=y-1;
if (mapp[x][y]=='#')y=y+1;
return find(x,y);
}
else if (mapp[x][y]=='R'){
ismap[x][y]=true;
y=y+1;
while(mapp[x][y]=='.')y=y+1;
if (mapp[x][y]=='#')y=y-1;
return find(x,y);
}
return 0;
}
int main(){
do{
memsett(mapp);
memsett(ismap);
memsett(loca);
//初始化
loca[0][0][1]=1;
//loca[0][0][0]表示步数;loca[0][0][1]表示个数;
cin>>a>>b;
if(a==0&&b==0)return 0;
forr(i,0,b+1){
mapp[0][i]='#';
mapp[a+1][i]='#';
}
forr(i,0,a+1){
mapp[i][0]='#';
mapp[i][b+1]='#';
}
getchar();
forr(i,1,a){
forr(j,1,b){
cin>>mapp[i][j];
if (mapp[i][j]=='S'){
loca[0][1][0]=i;
loca[0][1][1]=j;
ismap[i][j]=true;
}
if (mapp[i][j]=='T'){
final=i*10000+j;
}
}
getchar();
}
//读入
boo=false;
do {
loca[0][0][0]++;
loca[loca[0][0][0] % 2][0][1] = 0;
forr(i, 1, loca[(loca[0][0][0] + 1) % 2][0][1]) {
finded = find(loca[(loca[0][0][0] + 1) % 2][i][0] + 1, loca[(loca[0][0][0] + 1) % 2][i][1]);
if (finded == final) {
boo = true;
break;
}
if (finded != 0) {
loca[loca[0][0][0] % 2][0][1]++;
loca[loca[0][0][0] % 2][loca[loca[0][0][0] % 2][0][1]][0] = finded / 10000;
loca[loca[0][0][0] % 2][loca[loca[0][0][0] % 2][0][1]][1] = finded % 10000;
ismap[finded / 10000][finded % 10000] = true;
}
finded = find(loca[(loca[0][0][0] + 1) % 2][i][0] - 1, loca[(loca[0][0][0] + 1) % 2][i][1]);
if (finded == final) {
boo = true;
break;
}
if (finded != 0) {
loca[loca[0][0][0] % 2][0][1]++;
loca[loca[0][0][0] % 2][loca[loca[0][0][0] % 2][0][1]][0] = finded / 10000;
loca[loca[0][0][0] % 2][loca[loca[0][0][0] % 2][0][1]][1] = finded % 10000;
ismap[finded / 10000][finded % 10000] = true;
}
finded = find(loca[(loca[0][0][0] + 1) % 2][i][0], loca[(loca[0][0][0] + 1) % 2][i][1] + 1);
if (finded == final) {
boo = true;
break;
}
if (finded != 0) {
loca[loca[0][0][0] % 2][0][1]++;
loca[loca[0][0][0] % 2][loca[loca[0][0][0] % 2][0][1]][0] = finded / 10000;
loca[loca[0][0][0] % 2][loca[loca[0][0][0] % 2][0][1]][1] = finded % 10000;
ismap[finded / 10000][finded % 10000] = true;
}
finded = find(loca[(loca[0][0][0] + 1) % 2][i][0], loca[(loca[0][0][0] + 1) % 2][i][1] - 1);
if (finded == final) {
boo = true;
break;
}
if (finded != 0) {
loca[loca[0][0][0] % 2][0][1]++;
loca[loca[0][0][0] % 2][loca[loca[0][0][0] % 2][0][1]][0] = finded / 10000;
loca[loca[0][0][0] % 2][loca[loca[0][0][0] % 2][0][1]][1] = finded % 10000;
ismap[finded / 10000][finded % 10000] = true;
}
}
if (boo) {
cout << loca[0][0][0] << endl;
break;
}
if (loca[loca[0][0][0] % 2][0][1] == 0) {
cout << -1 << endl;
break;
}
}while(true);
}while(1);
}
同样暴力的dijkstra:
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pa;
const int N=4000005;
const int INF=0x3f3f3f3f;
const int dx[4]={0,0,1,-1};
const int dy[4]={1,-1,0,0};
int n,m,cnt,S,T;
char str[2005][2005];
int head[N],dis[N];
int Next[N<<2],List[N<<2],key[N<<2];
bool vis[N];
inline int read()
{
int a=0,f=1; char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
return a*f;
}
inline void insert(int x,int y,int z)
{
Next[++cnt]=head[x];
head[x]=cnt;
List[cnt]=y;
key[cnt]=z;
}
inline int id(int x,int y)
{
return (x-1)*m+y;
}
inline bool judge(int x,int y)
{
if (x<1||x>n||y<1||y>m||str[x][y]=='#') return false;
return true;
}
inline int dijkstra(int S,int T)
{
priority_queue<pa,vector<pa>,greater<pa> > q;
memset(dis,0x3f,sizeof(dis));
memset(vis,false,sizeof(vis));
q.push(make_pair(0,S));
dis[S]=0;
while (!q.empty())
{
int x=q.top().second; q.pop();
if (vis[x]) continue; vis[x]=true;
for (int i=head[x];i;i=Next[i])
if (dis[List[i]]>dis[x]+key[i])
{
dis[List[i]]=dis[x]+key[i];
q.push(make_pair(dis[List[i]],List[i]));
}
}
return dis[T]==INF?-1:dis[T];
}
int main()
{
n=read(); m=read();
while (n||m)
{
cnt=0;
memset(head,0,sizeof(head));
for (int i=1;i<=n;i++)
scanf("%s",str[i]+1);
for (int i=0;i<=m+1;i++)
str[0][i]=str[n+1][i]='#';
for (int i=0;i<=n+1;i++)
str[i][0]=str[i][m+1]='#';
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
if (str[i][j]=='#') continue;
if (str[i][j]=='.'||str[i][j]=='S')
for (int k=0;k<4;k++)
{
int nx=i+dx[k],ny=j+dy[k];
if (judge(nx,ny))
insert(id(i,j),id(nx,ny),1);
}
if (str[i][j]=='S') S=id(i,j);
if (str[i][j]=='T') T=id(i,j);
}
for (int i=1;i<=n;i++)
{
int now=-1;
for (int j=0;j<=m;j++)
{
if (str[i][j]=='L')
{
if (now==-1) continue;
insert(id(i,j),id(i,now),0);
}
if (str[i][j]=='#') now=j+1;
else if (str[i][j]!='.'&&str[i][j]!='S') now=j;
}
now=-1;
for (int j=m+1;j;j--)
{
if (str[i][j]=='R')
{
if (now==-1) continue;
insert(id(i,j),id(i,now),0);
}
if (str[i][j]=='#') now=j-1;
else if (str[i][j]!='.'&&str[i][j]!='S') now=j;
}
}
for (int i=1;i<=m;i++)
{
int now=-1;
for (int j=0;j<=n;j++)
{
if (str[j][i]=='U')
{
if (now==-1) continue;
insert(id(j,i),id(now,i),0);
}
if (str[j][i]=='#') now=j+1;
else if (str[j][i]!='.'&&str[j][i]!='S') now=j;
}
now=-1;
for (int j=n+1;j;j--)
{
if (str[j][i]=='D')
{
if (now==-1) continue;
insert(id(j,i),id(now,i),0);
}
if (str[j][i]=='#') now=j-1;
else if (str[j][i]!='.'&&str[j][i]!='S') now=j;
}
}
cout<<dijkstra(S,T)<<endl;
n=read(); m=read();
}
return 0;
}